fix: 更新oauth相关数据的组件优化,允许未提供refreshEndpoint
This commit is contained in:
parent
a3ec5fc808
commit
1e5697a28e
|
|
@ -1,2 +1,6 @@
|
|||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../../oak-app-domain").EntityDict, "oauthApplication", false, {}>) => React.ReactElement;
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../../oak-app-domain").EntityDict, "oauthApplication", false, {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export default OakComponent({
|
|||
name: 1,
|
||||
description: 1,
|
||||
redirectUris: 1,
|
||||
logo: 1,
|
||||
logo: 1, // string
|
||||
isConfidential: 1,
|
||||
scopes: 1,
|
||||
scopes: 1, // string[]
|
||||
ableState: 1,
|
||||
requirePKCE: 1,
|
||||
},
|
||||
|
|
@ -30,7 +30,11 @@ export default OakComponent({
|
|||
isCreation: this.isCreation(),
|
||||
};
|
||||
},
|
||||
properties: {},
|
||||
properties: {
|
||||
open: false,
|
||||
onCancel: (() => { }),
|
||||
onOk: (() => { }),
|
||||
},
|
||||
methods: {
|
||||
reGenerateClientSecret() {
|
||||
this.features.cache.operate("oauthApplication", {
|
||||
|
|
@ -41,6 +45,15 @@ export default OakComponent({
|
|||
id: this.props.oakId,
|
||||
}
|
||||
});
|
||||
},
|
||||
setHideModal(hide) {
|
||||
this.setState({
|
||||
hideModal: hide
|
||||
});
|
||||
}
|
||||
},
|
||||
data: {
|
||||
hideModal: false,
|
||||
isCreation: false,
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"oauthApplication": "OAuth应用",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入应用名称",
|
||||
|
|
@ -21,5 +22,8 @@
|
|||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。",
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看"
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看",
|
||||
"create": "创建",
|
||||
"update": "更新",
|
||||
"cancel": "取消"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,12 @@ declare const Upsert: (props: WebComponentProps<EntityDict, "oauthApplication",
|
|||
item: RowWithActions<EntityDict, "oauthApplication">;
|
||||
clientSecret: string;
|
||||
isCreation: boolean;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
hideModal: boolean;
|
||||
}, {
|
||||
reGenerateClientSecret: () => void;
|
||||
}>) => React.JSX.Element;
|
||||
setHideModal: (hide: boolean) => void;
|
||||
}>) => React.JSX.Element | null;
|
||||
export default Upsert;
|
||||
|
|
|
|||
|
|
@ -1,41 +1,75 @@
|
|||
import React from 'react';
|
||||
import { Form, Input, Switch, Button, Space, Select } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Form, Input, Switch, Button, Space, Select, Modal } from 'antd';
|
||||
import Styles from './styles.module.less';
|
||||
const Upsert = (props) => {
|
||||
const { item, clientSecret, isCreation } = props.data;
|
||||
const { t, update, reGenerateClientSecret } = props.methods;
|
||||
const { item, clientSecret, isCreation, open, onCancel, onOk, hideModal } = props.data;
|
||||
const { t, update, reGenerateClientSecret, setHideModal } = props.methods;
|
||||
const [form] = Form.useForm();
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
form.setFieldsValue({
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
logo: item.logo,
|
||||
redirectUris: Array.isArray(item.redirectUris) ? item.redirectUris.join('\n') : '',
|
||||
scopes: item.scopes,
|
||||
isConfidential: item.isConfidential,
|
||||
ableState: item.ableState === 'enabled',
|
||||
requirePKCE: item.requirePKCE,
|
||||
});
|
||||
}
|
||||
}, [item, form]);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onOk();
|
||||
}, 300);
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Validation failed:', error);
|
||||
}
|
||||
};
|
||||
const handleCancel = () => {
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onCancel();
|
||||
}, 300);
|
||||
};
|
||||
if (item === undefined) {
|
||||
return <div>{t('noData')}</div>;
|
||||
return null;
|
||||
}
|
||||
return (<div className={Styles.id}>
|
||||
<Form layout="vertical" autoComplete="off">
|
||||
<Form.Item label={t('name')} rules={[{ required: true, message: t('nameRequired') }]}>
|
||||
<Input placeholder={t('namePlaceholder')} value={item.name || ""} onChange={(v) => {
|
||||
return (<Modal open={open && !hideModal} destroyOnClose={true} width={600} onCancel={handleCancel} onOk={handleOk} title={t('oauthApplication')} okText={isCreation ? t('create') : t('update')} cancelText={t('cancel')}>
|
||||
<div className={Styles.id}>
|
||||
<Form form={form} layout="vertical" autoComplete="off">
|
||||
<Form.Item label={t('name')} name="name" rules={[{ required: true, message: t('nameRequired') }]}>
|
||||
<Input placeholder={t('namePlaceholder')} onChange={(v) => {
|
||||
update({ name: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('description')}>
|
||||
<Input.TextArea placeholder={t('descriptionPlaceholder')} value={item.description || ""} rows={4} onChange={(v) => {
|
||||
<Form.Item label={t('description')} name="description">
|
||||
<Input.TextArea placeholder={t('descriptionPlaceholder')} rows={4} onChange={(v) => {
|
||||
update({ description: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('logo')}>
|
||||
<Input placeholder={t('logoPlaceholder')} value={item.logo || ""} onChange={(v) => {
|
||||
<Form.Item label={t('logo')} name="logo">
|
||||
<Input placeholder={t('logoPlaceholder')} onChange={(v) => {
|
||||
update({ logo: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('redirectUris')} rules={[{ required: true, message: t('redirectUrisRequired') }]}>
|
||||
<Input.TextArea placeholder={t('redirectUrisPlaceholder')} value={Array.isArray(item.redirectUris) ? item.redirectUris.join('\n') : ""} rows={3} onChange={(v) => {
|
||||
<Form.Item label={t('redirectUris')} name="redirectUris" rules={[{ required: true, message: t('redirectUrisRequired') }]}>
|
||||
<Input.TextArea placeholder={t('redirectUrisPlaceholder')} rows={3} onChange={(v) => {
|
||||
const uris = v.target.value.split('\n').filter(uri => uri.trim() !== '');
|
||||
update({ redirectUris: uris });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('scopes')}>
|
||||
<Select mode="tags" placeholder={t('scopesPlaceholder')} value={item.scopes || []} onChange={(v) => {
|
||||
<Form.Item label={t('scopes')} name="scopes">
|
||||
<Select mode="tags" placeholder={t('scopesPlaceholder')} onChange={(v) => {
|
||||
update({ scopes: v });
|
||||
}} tokenSeparators={[',']} open={false}/>
|
||||
</Form.Item>
|
||||
|
|
@ -56,19 +90,20 @@ const Upsert = (props) => {
|
|||
</Space.Compact>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('isConfidential')} valuePropName="checked">
|
||||
<Switch checked={!!item.isConfidential} onChange={(checked) => update({ isConfidential: checked })}/>
|
||||
<Form.Item label={t('isConfidential')} name="isConfidential" valuePropName="checked">
|
||||
<Switch onChange={(checked) => update({ isConfidential: checked })}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('ableState')} valuePropName="checked">
|
||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||
<Form.Item label={t('ableState')} name="ableState" valuePropName="checked">
|
||||
<Switch onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||
</Form.Item>
|
||||
|
||||
{/* requirePKCE */}
|
||||
<Form.Item label={t('requirePKCE')} valuePropName="checked" tooltip={t('requirePKCETooltip')}>
|
||||
<Switch checked={!!item.requirePKCE} onChange={(checked) => update({ requirePKCE: checked })}/>
|
||||
<Form.Item label={t('requirePKCE')} name="requirePKCE" valuePropName="checked" tooltip={t('requirePKCETooltip')}>
|
||||
<Switch onChange={(checked) => update({ requirePKCE: checked })}/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>);
|
||||
</div>
|
||||
</Modal>);
|
||||
};
|
||||
export default Upsert;
|
||||
|
|
|
|||
|
|
@ -43,16 +43,13 @@ const OauthProvider = (props) => {
|
|||
{t('common::action.create')}
|
||||
</Button>
|
||||
</div>}/>)}
|
||||
{/* antd model */}
|
||||
<Modal open={!!upsertId} destroyOnClose={true} width={600} onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}>
|
||||
{upsertId && <AppUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId}/>}
|
||||
</Modal>
|
||||
{upsertId && (<AppUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} open={!!upsertId} onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}/>)}
|
||||
</>);
|
||||
};
|
||||
export default OauthProvider;
|
||||
|
|
|
|||
|
|
@ -1,2 +1,6 @@
|
|||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../../oak-app-domain").EntityDict, "oauthProvider", false, {}>) => React.ReactElement;
|
||||
declare const _default: (props: import("oak-frontend-base").ReactComponentProps<import("../../../../../oak-app-domain").EntityDict, "oauthProvider", false, {
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
}>) => React.ReactElement;
|
||||
export default _default;
|
||||
|
|
|
|||
|
|
@ -20,11 +20,26 @@ export default OakComponent({
|
|||
formData({ data }) {
|
||||
return {
|
||||
item: data,
|
||||
isCreation: this.isCreation(),
|
||||
};
|
||||
},
|
||||
properties: {},
|
||||
properties: {
|
||||
open: false,
|
||||
onCancel: (() => { }),
|
||||
onOk: (() => { }),
|
||||
},
|
||||
lifetimes: {
|
||||
ready() {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setHideModal(hide) {
|
||||
this.setState({
|
||||
hideModal: hide
|
||||
});
|
||||
}
|
||||
},
|
||||
data: {
|
||||
hideModal: false
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"oauthProvider": "OAuth提供商",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入OAuth提供商名称",
|
||||
|
|
@ -34,5 +35,7 @@
|
|||
"scopes": "权限范围",
|
||||
"scopesPlaceholder": "请选择或输入权限范围",
|
||||
"refreshEndpoint": "刷新端点",
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL"
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,5 +3,12 @@ import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
|||
import { EntityDict } from '../../../../../oak-app-domain';
|
||||
declare const Upsert: (props: WebComponentProps<EntityDict, "oauthProvider", false, {
|
||||
item: RowWithActions<EntityDict, "oauthProvider">;
|
||||
}>) => React.JSX.Element;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
hideModal: boolean;
|
||||
isCreation: boolean;
|
||||
}, {
|
||||
setHideModal: (hide: boolean) => void;
|
||||
}>) => React.JSX.Element | null;
|
||||
export default Upsert;
|
||||
|
|
|
|||
|
|
@ -1,26 +1,65 @@
|
|||
import React from 'react';
|
||||
import { Form, Input, Switch, Select, Typography } from 'antd';
|
||||
import React, { useEffect } from 'react';
|
||||
import { Form, Input, Switch, Select, Typography, Modal } from 'antd';
|
||||
import Styles from './styles.module.less';
|
||||
const { Text } = Typography;
|
||||
const Upsert = (props) => {
|
||||
const { item } = props.data;
|
||||
const { t, update } = props.methods;
|
||||
const { item, open, onCancel, onOk, hideModal, isCreation } = props.data;
|
||||
const { t, update, setHideModal } = props.methods;
|
||||
const [form] = Form.useForm();
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
form.setFieldsValue({
|
||||
name: item.name,
|
||||
type: item.type ? [item.type] : [],
|
||||
logo: item.logo,
|
||||
authorizationEndpoint: item.authorizationEndpoint,
|
||||
tokenEndpoint: item.tokenEndpoint,
|
||||
refreshEndpoint: item.refreshEndpoint,
|
||||
userInfoEndpoint: item.userInfoEndpoint,
|
||||
revokeEndpoint: item.revokeEndpoint,
|
||||
clientId: item.clientId,
|
||||
clientSecret: item.clientSecret,
|
||||
scopes: item.scopes,
|
||||
redirectUri: item.redirectUri,
|
||||
autoRegister: item.autoRegister,
|
||||
ableState: item.ableState === 'enabled',
|
||||
});
|
||||
}
|
||||
}, [item, form]);
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onOk();
|
||||
}, 300);
|
||||
}
|
||||
catch (error) {
|
||||
console.log('Validation failed:', error);
|
||||
}
|
||||
};
|
||||
const handleCancel = () => {
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onCancel();
|
||||
}, 300);
|
||||
};
|
||||
if (item === undefined) {
|
||||
return <div>{t('noData')}</div>;
|
||||
return null;
|
||||
}
|
||||
return (<div className={Styles.id}>
|
||||
<Form layout="vertical" autoComplete="off">
|
||||
<Form.Item label={t('name')} rules={[{ required: true, message: t('nameRequired') }]}>
|
||||
<Input placeholder={t('namePlaceholder')} value={item.name || ""} onChange={(v) => {
|
||||
return (<Modal open={open && !hideModal} destroyOnClose={true} width={600} onCancel={handleCancel} onOk={handleOk} title={t('oauthProvider')} okText={isCreation ? t('create') : t('update')} cancelText={t('cancel')}>
|
||||
<div className={Styles.id}>
|
||||
<Form form={form} layout="vertical" autoComplete="off">
|
||||
<Form.Item label={t('name')} name="name" rules={[{ required: true, message: t('nameRequired') }]}>
|
||||
<Input placeholder={t('namePlaceholder')} onChange={(v) => {
|
||||
update({ name: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('type')} rules={[{ required: true, message: t('typeRequired') }]} extra={item.type && item.type !== 'oak' && item.type !== 'gitea' ? (<Text type="warning">
|
||||
<Form.Item label={t('type')} name="type" rules={[{ required: true, message: t('typeRequired') }]} extra={item.type && item.type !== 'oak' && item.type !== 'gitea' ? (<Text type="warning">
|
||||
「{item.type}」不是预设类型,请自行注入 handler。
|
||||
</Text>) : undefined}>
|
||||
<Select mode="tags" placeholder={t('typePlaceholder')} value={item.type ? [item.type] : []} // 保持数组形式
|
||||
onChange={(v) => {
|
||||
<Select mode="tags" placeholder={t('typePlaceholder')} onChange={(v) => {
|
||||
// 只取最后一个输入或选择的值
|
||||
const last = v.slice(-1)[0];
|
||||
update({ type: last });
|
||||
|
|
@ -31,85 +70,76 @@ const Upsert = (props) => {
|
|||
]}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('logo')}>
|
||||
<Input placeholder={t('logoPlaceholder')} value={item.logo || ""} onChange={(v) => {
|
||||
<Form.Item label={t('logo')} name="logo">
|
||||
<Input placeholder={t('logoPlaceholder')} onChange={(v) => {
|
||||
update({ logo: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('authorizationEndpoint')} rules={[{ required: true, message: t('authorizationEndpointRequired') }]}>
|
||||
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""} onChange={(v) => {
|
||||
<Form.Item label={t('authorizationEndpoint')} name="authorizationEndpoint" rules={[{ required: true, message: t('authorizationEndpointRequired') }]}>
|
||||
<Input placeholder={t('authorizationEndpointPlaceholder')} onChange={(v) => {
|
||||
update({ authorizationEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('tokenEndpoint')} rules={[{ required: true, message: t('tokenEndpointRequired') }]}>
|
||||
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""} onChange={(v) => {
|
||||
<Form.Item label={t('tokenEndpoint')} name="tokenEndpoint" rules={[{ required: true, message: t('tokenEndpointRequired') }]}>
|
||||
<Input placeholder={t('tokenEndpointPlaceholder')} onChange={(v) => {
|
||||
update({ tokenEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('refreshEndpoint')}>
|
||||
<Input placeholder={t('refreshEndpointPlaceholder')} value={item.refreshEndpoint || ""} onChange={(v) => {
|
||||
<Form.Item label={t('refreshEndpoint')} name="refreshEndpoint">
|
||||
<Input placeholder={t('refreshEndpointPlaceholder')} onChange={(v) => {
|
||||
update({ refreshEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('userInfoEndpoint')}>
|
||||
<Input placeholder={t('userInfoEndpointPlaceholder')} value={item.userInfoEndpoint || ""} onChange={(v) => {
|
||||
<Form.Item label={t('userInfoEndpoint')} name="userInfoEndpoint">
|
||||
<Input placeholder={t('userInfoEndpointPlaceholder')} onChange={(v) => {
|
||||
update({ userInfoEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('revokeEndpoint')}>
|
||||
<Input placeholder={t('revokeEndpointPlaceholder')} value={item.revokeEndpoint || ""} onChange={(v) => {
|
||||
<Form.Item label={t('revokeEndpoint')} name="revokeEndpoint">
|
||||
<Input placeholder={t('revokeEndpointPlaceholder')} onChange={(v) => {
|
||||
update({ revokeEndpoint: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('clientId')} rules={[{ required: true, message: t('clientIdRequired') }]}>
|
||||
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""} onChange={(v) => {
|
||||
<Form.Item label={t('clientId')} name="clientId" rules={[{ required: true, message: t('clientIdRequired') }]}>
|
||||
<Input placeholder={t('clientIdPlaceholder')} onChange={(v) => {
|
||||
update({ clientId: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('clientSecret')} rules={[{ required: true, message: t('clientSecretRequired') }]}>
|
||||
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""} onChange={(v) => {
|
||||
<Form.Item label={t('clientSecret')} name="clientSecret" rules={[{ required: true, message: t('clientSecretRequired') }]}>
|
||||
<Input.Password placeholder={t('clientSecretPlaceholder')} onChange={(v) => {
|
||||
update({ clientSecret: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('scopes')}>
|
||||
<Select mode="tags" placeholder={t('scopesPlaceholder')} value={item.scopes || []} onChange={(v) => {
|
||||
<Form.Item label={t('scopes')} name="scopes">
|
||||
<Select mode="tags" placeholder={t('scopesPlaceholder')} onChange={(v) => {
|
||||
update({ scopes: v });
|
||||
}} tokenSeparators={[',']} open={false}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('redirectUri')} rules={[{ required: true, message: t('redirectUriRequired') }]}>
|
||||
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""} onChange={(v) => {
|
||||
<Form.Item label={t('redirectUri')} name="redirectUri" rules={[{ required: true, message: t('redirectUriRequired') }]}>
|
||||
<Input placeholder={t('redirectUriPlaceholder')} onChange={(v) => {
|
||||
update({ redirectUri: v.target.value });
|
||||
}}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('autoRegister')} valuePropName="checked">
|
||||
<Switch checked={!!item.autoRegister} onChange={(checked) => update({ autoRegister: checked })}/>
|
||||
<Form.Item label={t('autoRegister')} name="autoRegister" valuePropName="checked">
|
||||
<Switch onChange={(checked) => update({ autoRegister: checked })}/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={t('ableState')} valuePropName="checked">
|
||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||
<Form.Item label={t('ableState')} name="ableState" valuePropName="checked">
|
||||
<Switch onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}/>
|
||||
</Form.Item>
|
||||
|
||||
{/* <Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t('confirm')}
|
||||
</Button>
|
||||
<Button onClick={handleCancel}>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item> */}
|
||||
</Form>
|
||||
</div>);
|
||||
</div>
|
||||
</Modal>);
|
||||
};
|
||||
export default Upsert;
|
||||
|
|
|
|||
|
|
@ -44,16 +44,13 @@ const OauthProvider = (props) => {
|
|||
{t('common::action.create')}
|
||||
</Button>
|
||||
</div>}/>)}
|
||||
{/* antd model */}
|
||||
<Modal open={!!upsertId} destroyOnClose={true} width={600} onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}>
|
||||
{upsertId && <ProviderUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId}/>}
|
||||
</Modal>
|
||||
{upsertId && (<ProviderUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} open={!!upsertId} onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}/>)}
|
||||
</>);
|
||||
};
|
||||
export default OauthProvider;
|
||||
|
|
|
|||
|
|
@ -380,6 +380,7 @@ const i18ns = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthApps/upsert",
|
||||
data: {
|
||||
"oauthApplication": "OAuth应用",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入应用名称",
|
||||
|
|
@ -402,7 +403,10 @@ const i18ns = [
|
|||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。",
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看"
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看",
|
||||
"create": "创建",
|
||||
"update": "更新",
|
||||
"cancel": "取消"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -426,6 +430,7 @@ const i18ns = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthProvider/upsert",
|
||||
data: {
|
||||
"oauthProvider": "OAuth提供商",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入OAuth提供商名称",
|
||||
|
|
@ -461,7 +466,9 @@ const i18ns = [
|
|||
"scopes": "权限范围",
|
||||
"scopesPlaceholder": "请选择或输入权限范围",
|
||||
"refreshEndpoint": "刷新端点",
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL"
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -143,7 +143,10 @@ const triggers = [
|
|||
assert(oauthUser.state, `oauthUser ${oauthUser.id} 关联的 state 不存在`);
|
||||
assert(oauthUser.state.provider, `oauthUser ${oauthUser.id} 关联的 state 的 provider 不存在`);
|
||||
const refreshEndpoint = oauthUser.state.provider.refreshEndpoint;
|
||||
assert(refreshEndpoint, `oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌`);
|
||||
if (!refreshEndpoint) {
|
||||
console.warn(`oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌,跳过`);
|
||||
continue;
|
||||
}
|
||||
// 根据 RFC 6749 规范 使用 refresh token 刷新 access token
|
||||
const authHeaderRaw = Buffer.from(`${oauthUser.state.provider.clientId}:${oauthUser.state.provider.clientSecret}`).toString('base64');
|
||||
const resp = await fetch(refreshEndpoint, {
|
||||
|
|
|
|||
|
|
@ -382,6 +382,7 @@ const i18ns = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthApps/upsert",
|
||||
data: {
|
||||
"oauthApplication": "OAuth应用",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入应用名称",
|
||||
|
|
@ -404,7 +405,10 @@ const i18ns = [
|
|||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。",
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看"
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看",
|
||||
"create": "创建",
|
||||
"update": "更新",
|
||||
"cancel": "取消"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -428,6 +432,7 @@ const i18ns = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthProvider/upsert",
|
||||
data: {
|
||||
"oauthProvider": "OAuth提供商",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入OAuth提供商名称",
|
||||
|
|
@ -463,7 +468,9 @@ const i18ns = [
|
|||
"scopes": "权限范围",
|
||||
"scopesPlaceholder": "请选择或输入权限范围",
|
||||
"refreshEndpoint": "刷新端点",
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL"
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -146,7 +146,10 @@ const triggers = [
|
|||
(0, assert_1.default)(oauthUser.state, `oauthUser ${oauthUser.id} 关联的 state 不存在`);
|
||||
(0, assert_1.default)(oauthUser.state.provider, `oauthUser ${oauthUser.id} 关联的 state 的 provider 不存在`);
|
||||
const refreshEndpoint = oauthUser.state.provider.refreshEndpoint;
|
||||
(0, assert_1.default)(refreshEndpoint, `oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌`);
|
||||
if (!refreshEndpoint) {
|
||||
console.warn(`oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌,跳过`);
|
||||
continue;
|
||||
}
|
||||
// 根据 RFC 6749 规范 使用 refresh token 刷新 access token
|
||||
const authHeaderRaw = Buffer.from(`${oauthUser.state.provider.clientId}:${oauthUser.state.provider.clientSecret}`).toString('base64');
|
||||
const resp = await fetch(refreshEndpoint, {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,11 @@ export default OakComponent({
|
|||
isCreation: this.isCreation(),
|
||||
};
|
||||
},
|
||||
properties: {},
|
||||
properties: {
|
||||
open: false as boolean,
|
||||
onCancel: (() => {}) as () => void,
|
||||
onOk: (() => {}) as () => void,
|
||||
},
|
||||
methods: {
|
||||
reGenerateClientSecret() {
|
||||
this.features.cache.operate("oauthApplication", {
|
||||
|
|
@ -42,6 +46,15 @@ export default OakComponent({
|
|||
id: this.props.oakId,
|
||||
}
|
||||
})
|
||||
},
|
||||
setHideModal(hide: boolean) {
|
||||
this.setState({
|
||||
hideModal: hide
|
||||
});
|
||||
}
|
||||
},
|
||||
data: {
|
||||
hideModal: false,
|
||||
isCreation: false,
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"oauthApplication": "OAuth应用",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入应用名称",
|
||||
|
|
@ -21,5 +22,8 @@
|
|||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。",
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看"
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看",
|
||||
"create": "创建",
|
||||
"update": "更新",
|
||||
"cancel": "取消"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select } from 'antd';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select, Modal } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||
import Styles from './styles.module.less';
|
||||
|
|
@ -14,32 +14,83 @@ const Upsert = (
|
|||
item: RowWithActions<EntityDict, 'oauthApplication'>;
|
||||
clientSecret: string;
|
||||
isCreation: boolean;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
hideModal: boolean;
|
||||
},
|
||||
{
|
||||
reGenerateClientSecret: () => void;
|
||||
setHideModal: (hide: boolean) => void;
|
||||
}
|
||||
>
|
||||
) => {
|
||||
const { item, clientSecret, isCreation } = props.data;
|
||||
const { t, update, reGenerateClientSecret } = props.methods;
|
||||
const { item, clientSecret, isCreation, open, onCancel, onOk, hideModal } = props.data;
|
||||
const { t, update, reGenerateClientSecret, setHideModal } = props.methods;
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
form.setFieldsValue({
|
||||
name: item.name,
|
||||
description: item.description,
|
||||
logo: item.logo,
|
||||
redirectUris: Array.isArray(item.redirectUris) ? item.redirectUris.join('\n') : '',
|
||||
scopes: item.scopes,
|
||||
isConfidential: item.isConfidential,
|
||||
ableState: item.ableState === 'enabled',
|
||||
requirePKCE: item.requirePKCE,
|
||||
});
|
||||
}
|
||||
}, [item, form]);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onOk();
|
||||
}, 300);
|
||||
} catch (error) {
|
||||
console.log('Validation failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onCancel();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
if (item === undefined) {
|
||||
return <div>{t('noData')}</div>;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={Styles.id}>
|
||||
<Modal
|
||||
open={open && !hideModal}
|
||||
destroyOnClose={true}
|
||||
width={600}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
title={t('oauthApplication')}
|
||||
okText={isCreation ? t('create') : t('update')}
|
||||
cancelText={t('cancel')}
|
||||
>
|
||||
<div className={Styles.id}>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item
|
||||
label={t('name')}
|
||||
name="name"
|
||||
rules={[{ required: true, message: t('nameRequired') }]}
|
||||
>
|
||||
<Input
|
||||
placeholder={t('namePlaceholder')}
|
||||
value={item.name || ""}
|
||||
placeholder={t('namePlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ name: v.target.value });
|
||||
}}
|
||||
|
|
@ -48,10 +99,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('description')}
|
||||
name="description"
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder={t('descriptionPlaceholder')}
|
||||
value={item.description || ""}
|
||||
placeholder={t('descriptionPlaceholder')}
|
||||
rows={4}
|
||||
onChange={(v) => {
|
||||
update({ description: v.target.value });
|
||||
|
|
@ -61,10 +112,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('logo')}
|
||||
name="logo"
|
||||
>
|
||||
<Input
|
||||
placeholder={t('logoPlaceholder')}
|
||||
value={item.logo || ""}
|
||||
placeholder={t('logoPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ logo: v.target.value });
|
||||
}}
|
||||
|
|
@ -73,11 +124,11 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('redirectUris')}
|
||||
name="redirectUris"
|
||||
rules={[{ required: true, message: t('redirectUrisRequired') }]}
|
||||
>
|
||||
<Input.TextArea
|
||||
placeholder={t('redirectUrisPlaceholder')}
|
||||
value={Array.isArray(item.redirectUris) ? item.redirectUris.join('\n') : ""}
|
||||
placeholder={t('redirectUrisPlaceholder')}
|
||||
rows={3}
|
||||
onChange={(v) => {
|
||||
const uris = v.target.value.split('\n').filter(uri => uri.trim() !== '');
|
||||
|
|
@ -88,11 +139,11 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('scopes')}
|
||||
name="scopes"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('scopesPlaceholder')}
|
||||
value={item.scopes || []}
|
||||
onChange={(v) => {
|
||||
update({ scopes: v });
|
||||
}}
|
||||
|
|
@ -135,20 +186,20 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('isConfidential')}
|
||||
name="isConfidential"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch
|
||||
checked={!!item.isConfidential}
|
||||
onChange={(checked) => update({ isConfidential: checked })}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('ableState')}
|
||||
name="ableState"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch
|
||||
checked={!!item.ableState}
|
||||
onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
|
@ -156,16 +207,17 @@ const Upsert = (
|
|||
{/* requirePKCE */}
|
||||
<Form.Item
|
||||
label={t('requirePKCE')}
|
||||
name="requirePKCE"
|
||||
valuePropName="checked"
|
||||
tooltip={t('requirePKCETooltip')}
|
||||
>
|
||||
<Switch
|
||||
checked={!!item.requirePKCE}
|
||||
onChange={(checked) => update({ requirePKCE: checked })}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -78,16 +78,21 @@ const OauthProvider = (
|
|||
}
|
||||
/>
|
||||
)}
|
||||
{/* antd model */}
|
||||
<Modal open={!!upsertId} destroyOnClose={true} width={600} onCancel={() => {
|
||||
clean()
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute()
|
||||
setUpsertId(null);
|
||||
}}>
|
||||
{upsertId && <AppUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} />}
|
||||
</Modal>
|
||||
{upsertId && (
|
||||
<AppUpsert
|
||||
oakPath={`${oakFullpath}.${upsertId}`}
|
||||
oakId={upsertId}
|
||||
open={!!upsertId}
|
||||
onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}}
|
||||
onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,11 +20,26 @@ export default OakComponent({
|
|||
formData({ data }) {
|
||||
return {
|
||||
item: data,
|
||||
isCreation: this.isCreation(),
|
||||
};
|
||||
},
|
||||
properties: {},
|
||||
properties: {
|
||||
open: false as boolean,
|
||||
onCancel: (() => { }) as () => void,
|
||||
onOk: (() => { }) as () => void,
|
||||
},
|
||||
lifetimes: {
|
||||
ready() {
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
setHideModal(hide: boolean) {
|
||||
this.setState({
|
||||
hideModal: hide
|
||||
});
|
||||
}
|
||||
},
|
||||
data: {
|
||||
hideModal: false
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
{
|
||||
"oauthProvider": "OAuth提供商",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入OAuth提供商名称",
|
||||
|
|
@ -34,5 +35,7 @@
|
|||
"scopes": "权限范围",
|
||||
"scopesPlaceholder": "请选择或输入权限范围",
|
||||
"refreshEndpoint": "刷新端点",
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL"
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select, Typography } from 'antd';
|
||||
import { Form, Input, Switch, Button, Space, Upload, Select, Typography, Modal } from 'antd';
|
||||
import { UploadOutlined } from '@ant-design/icons';
|
||||
import { RowWithActions, WebComponentProps } from 'oak-frontend-base';
|
||||
import Styles from './styles.module.less';
|
||||
|
|
@ -13,27 +13,88 @@ const Upsert = (
|
|||
false,
|
||||
{
|
||||
item: RowWithActions<EntityDict, 'oauthProvider'>;
|
||||
open: boolean;
|
||||
onCancel: () => void;
|
||||
onOk: () => void;
|
||||
hideModal: boolean;
|
||||
isCreation: boolean;
|
||||
},
|
||||
{
|
||||
setHideModal: (hide: boolean) => void;
|
||||
}
|
||||
>
|
||||
) => {
|
||||
const { item } = props.data;
|
||||
const { t, update } = props.methods;
|
||||
const { item, open, onCancel, onOk, hideModal, isCreation } = props.data;
|
||||
const { t, update, setHideModal } = props.methods;
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (item) {
|
||||
form.setFieldsValue({
|
||||
name: item.name,
|
||||
type: item.type ? [item.type] : [],
|
||||
logo: item.logo,
|
||||
authorizationEndpoint: item.authorizationEndpoint,
|
||||
tokenEndpoint: item.tokenEndpoint,
|
||||
refreshEndpoint: item.refreshEndpoint,
|
||||
userInfoEndpoint: item.userInfoEndpoint,
|
||||
revokeEndpoint: item.revokeEndpoint,
|
||||
clientId: item.clientId,
|
||||
clientSecret: item.clientSecret,
|
||||
scopes: item.scopes,
|
||||
redirectUri: item.redirectUri,
|
||||
autoRegister: item.autoRegister,
|
||||
ableState: item.ableState === 'enabled',
|
||||
});
|
||||
}
|
||||
}, [item, form]);
|
||||
|
||||
const handleOk = async () => {
|
||||
try {
|
||||
await form.validateFields();
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onOk();
|
||||
}, 300);
|
||||
} catch (error) {
|
||||
console.log('Validation failed:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCancel = () => {
|
||||
setHideModal(true);
|
||||
setTimeout(() => {
|
||||
onCancel();
|
||||
}, 300);
|
||||
}
|
||||
|
||||
if (item === undefined) {
|
||||
return <div>{t('noData')}</div>;
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={Styles.id}>
|
||||
<Modal
|
||||
open={open && !hideModal}
|
||||
destroyOnClose={true}
|
||||
width={600}
|
||||
onCancel={handleCancel}
|
||||
onOk={handleOk}
|
||||
title={t('oauthProvider')}
|
||||
okText={isCreation ? t('create') : t('update')}
|
||||
cancelText={t('cancel')}
|
||||
>
|
||||
<div className={Styles.id}>
|
||||
<Form
|
||||
form={form}
|
||||
layout="vertical"
|
||||
autoComplete="off"
|
||||
>
|
||||
<Form.Item
|
||||
label={t('name')}
|
||||
name="name"
|
||||
rules={[{ required: true, message: t('nameRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('namePlaceholder')} value={item.name || ""}
|
||||
<Input placeholder={t('namePlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ name: v.target.value });
|
||||
}}
|
||||
|
|
@ -42,6 +103,7 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('type')}
|
||||
name="type"
|
||||
rules={[{ required: true, message: t('typeRequired') }]}
|
||||
extra={
|
||||
item.type && item.type !== 'oak' && item.type !== 'gitea' ? (
|
||||
|
|
@ -54,7 +116,6 @@ const Upsert = (
|
|||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('typePlaceholder')}
|
||||
value={item.type ? [item.type] : []} // 保持数组形式
|
||||
onChange={(v) => {
|
||||
// 只取最后一个输入或选择的值
|
||||
const last = v.slice(-1)[0] as
|
||||
|
|
@ -77,8 +138,9 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('logo')}
|
||||
name="logo"
|
||||
>
|
||||
<Input placeholder={t('logoPlaceholder')} value={item.logo || ""}
|
||||
<Input placeholder={t('logoPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ logo: v.target.value });
|
||||
}}
|
||||
|
|
@ -87,9 +149,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('authorizationEndpoint')}
|
||||
name="authorizationEndpoint"
|
||||
rules={[{ required: true, message: t('authorizationEndpointRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('authorizationEndpointPlaceholder')} value={item.authorizationEndpoint || ""}
|
||||
<Input placeholder={t('authorizationEndpointPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ authorizationEndpoint: v.target.value });
|
||||
}}
|
||||
|
|
@ -98,9 +161,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('tokenEndpoint')}
|
||||
name="tokenEndpoint"
|
||||
rules={[{ required: true, message: t('tokenEndpointRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('tokenEndpointPlaceholder')} value={item.tokenEndpoint || ""}
|
||||
<Input placeholder={t('tokenEndpointPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ tokenEndpoint: v.target.value });
|
||||
}}
|
||||
|
|
@ -109,8 +173,9 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('refreshEndpoint')}
|
||||
name="refreshEndpoint"
|
||||
>
|
||||
<Input placeholder={t('refreshEndpointPlaceholder')} value={item.refreshEndpoint || ""}
|
||||
<Input placeholder={t('refreshEndpointPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ refreshEndpoint: v.target.value });
|
||||
}}
|
||||
|
|
@ -119,8 +184,9 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('userInfoEndpoint')}
|
||||
name="userInfoEndpoint"
|
||||
>
|
||||
<Input placeholder={t('userInfoEndpointPlaceholder')} value={item.userInfoEndpoint || ""}
|
||||
<Input placeholder={t('userInfoEndpointPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ userInfoEndpoint: v.target.value });
|
||||
}}
|
||||
|
|
@ -129,8 +195,9 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('revokeEndpoint')}
|
||||
name="revokeEndpoint"
|
||||
>
|
||||
<Input placeholder={t('revokeEndpointPlaceholder')} value={item.revokeEndpoint || ""}
|
||||
<Input placeholder={t('revokeEndpointPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ revokeEndpoint: v.target.value });
|
||||
}}
|
||||
|
|
@ -139,9 +206,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('clientId')}
|
||||
name="clientId"
|
||||
rules={[{ required: true, message: t('clientIdRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('clientIdPlaceholder')} value={item.clientId || ""}
|
||||
<Input placeholder={t('clientIdPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ clientId: v.target.value });
|
||||
}}
|
||||
|
|
@ -150,9 +218,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('clientSecret')}
|
||||
name="clientSecret"
|
||||
rules={[{ required: true, message: t('clientSecretRequired') }]}
|
||||
>
|
||||
<Input.Password placeholder={t('clientSecretPlaceholder')} value={item.clientSecret || ""}
|
||||
<Input.Password placeholder={t('clientSecretPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ clientSecret: v.target.value });
|
||||
}}
|
||||
|
|
@ -161,11 +230,11 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('scopes')}
|
||||
name="scopes"
|
||||
>
|
||||
<Select
|
||||
mode="tags"
|
||||
placeholder={t('scopesPlaceholder')}
|
||||
value={item.scopes || []}
|
||||
onChange={(v) => {
|
||||
update({ scopes: v });
|
||||
}}
|
||||
|
|
@ -176,9 +245,10 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('redirectUri')}
|
||||
name="redirectUri"
|
||||
rules={[{ required: true, message: t('redirectUriRequired') }]}
|
||||
>
|
||||
<Input placeholder={t('redirectUriPlaceholder')} value={item.redirectUri || ""}
|
||||
<Input placeholder={t('redirectUriPlaceholder')}
|
||||
onChange={(v) => {
|
||||
update({ redirectUri: v.target.value });
|
||||
}}
|
||||
|
|
@ -187,30 +257,23 @@ const Upsert = (
|
|||
|
||||
<Form.Item
|
||||
label={t('autoRegister')}
|
||||
name="autoRegister"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch checked={!!item.autoRegister} onChange={(checked) => update({ autoRegister: checked })} />
|
||||
<Switch onChange={(checked) => update({ autoRegister: checked })} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item
|
||||
label={t('ableState')}
|
||||
name="ableState"
|
||||
valuePropName="checked"
|
||||
>
|
||||
<Switch checked={!!item.ableState} onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })} />
|
||||
<Switch onChange={(checked) => update({ ableState: checked ? "enabled" : "disabled" })} />
|
||||
</Form.Item>
|
||||
|
||||
{/* <Form.Item>
|
||||
<Space>
|
||||
<Button type="primary" htmlType="submit">
|
||||
{t('confirm')}
|
||||
</Button>
|
||||
<Button onClick={handleCancel}>
|
||||
{t('cancel')}
|
||||
</Button>
|
||||
</Space>
|
||||
</Form.Item> */}
|
||||
</Form>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -79,16 +79,21 @@ const OauthProvider = (
|
|||
}
|
||||
/>
|
||||
)}
|
||||
{/* antd model */}
|
||||
<Modal open={!!upsertId} destroyOnClose={true} width={600} onCancel={() => {
|
||||
clean()
|
||||
setUpsertId(null);
|
||||
}} onOk={() => {
|
||||
execute()
|
||||
setUpsertId(null);
|
||||
}}>
|
||||
{upsertId && <ProviderUpsert oakPath={`${oakFullpath}.${upsertId}`} oakId={upsertId} />}
|
||||
</Modal>
|
||||
{upsertId && (
|
||||
<ProviderUpsert
|
||||
oakPath={`${oakFullpath}.${upsertId}`}
|
||||
oakId={upsertId}
|
||||
open={!!upsertId}
|
||||
onCancel={() => {
|
||||
clean();
|
||||
setUpsertId(null);
|
||||
}}
|
||||
onOk={() => {
|
||||
execute();
|
||||
setUpsertId(null);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -382,6 +382,7 @@ const i18ns: I18n[] = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthApps/upsert",
|
||||
data: {
|
||||
"oauthApplication": "OAuth应用",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入应用名称",
|
||||
|
|
@ -404,7 +405,10 @@ const i18ns: I18n[] = [
|
|||
"clientIdPlaceholder": "自动生成的客户端ID",
|
||||
"requirePKCE": "强制 PKCE",
|
||||
"requirePKCETooltip": "启用后,授权请求必须使用PKCE扩展以增强安全性。",
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看"
|
||||
"clientSecretTooltip": "客户端密钥仅在机密客户端中使用,用于身份验证。创建成功后可以查看",
|
||||
"create": "创建",
|
||||
"update": "更新",
|
||||
"cancel": "取消"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -428,6 +432,7 @@ const i18ns: I18n[] = [
|
|||
module: "oak-general-business",
|
||||
position: "src/components/oauth/management/oauthProvider/upsert",
|
||||
data: {
|
||||
"oauthProvider": "OAuth提供商",
|
||||
"name": "名称",
|
||||
"nameRequired": "请输入名称",
|
||||
"namePlaceholder": "请输入OAuth提供商名称",
|
||||
|
|
@ -463,7 +468,9 @@ const i18ns: I18n[] = [
|
|||
"scopes": "权限范围",
|
||||
"scopesPlaceholder": "请选择或输入权限范围",
|
||||
"refreshEndpoint": "刷新端点",
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL"
|
||||
"refreshEndpointPlaceholder": "请输入刷新端点URL",
|
||||
"create": "创建",
|
||||
"update": "更新"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -169,7 +169,10 @@ const triggers: Trigger<EntityDict, "oauthUser", BRC<EntityDict>>[] = [
|
|||
assert(oauthUser.state, `oauthUser ${oauthUser.id} 关联的 state 不存在`);
|
||||
assert(oauthUser.state.provider, `oauthUser ${oauthUser.id} 关联的 state 的 provider 不存在`);
|
||||
const refreshEndpoint = oauthUser.state.provider.refreshEndpoint;
|
||||
assert(refreshEndpoint, `oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌`);
|
||||
if (!refreshEndpoint) {
|
||||
console.warn(`oauthUser ${oauthUser.id} 关联的 provider 不支持刷新令牌,跳过`);
|
||||
continue;
|
||||
}
|
||||
|
||||
// 根据 RFC 6749 规范 使用 refresh token 刷新 access token
|
||||
const authHeaderRaw = Buffer.from(`${oauthUser.state.provider.clientId}:${oauthUser.state.provider.clientSecret}`).toString('base64');
|
||||
|
|
|
|||
Loading…
Reference in New Issue