优化应用

pull/50/MERGE
smallbun 2023-09-16 23:39:44 +08:00
parent b1a0693eac
commit ba47bef290
15 changed files with 154 additions and 2868 deletions

View File

@ -1,224 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { history } from '@@/core/history';
import { DesktopOutlined, ProfileOutlined, SafetyOutlined } from '@ant-design/icons';
import { GridContent, PageContainer } from '@ant-design/pro-components';
import { useAsyncEffect } from 'ahooks';
import type { MenuProps } from 'antd';
import { App, Menu } from 'antd';
import React, { useLayoutEffect, useRef, useState } from 'react';
import AccessPolicy from './components/AccessPolicy';
import AppAccount from './components/AppAccount';
import AppBasic from './components/AppBasic';
import { ConfigTabs } from './constant';
import AppProtocol from './components/AppProtocol';
import queryString from 'query-string';
import { useIntl, useLocation } from '@umijs/max';
import useStyle from './style';
import classNames from 'classnames';
import { AppProtocolType } from '@/constant';
import PermissionResource from './components/PermissionResource';
import PermissionRole from './components/PermissionRole';
const prefixCls = 'app-config';
export default () => {
const [keys, setKeys] = useState<string[]>();
const [title, setTitle] = useState<string>();
const location = useLocation();
const { wrapSSR, hashId } = useStyle(prefixCls);
const intl = useIntl();
const { message } = App.useApp();
const [initConfig, setInitConfig] = useState<{ mode: 'horizontal' | 'vertical' | 'inline' }>({
mode: 'inline',
});
const dom = useRef<HTMLDivElement>();
const resize = () => {
requestAnimationFrame(() => {
if (!dom.current) {
return;
}
let mode: 'inline' | 'horizontal' = 'inline';
if (window.innerWidth < 1200) {
mode = 'horizontal';
}
setInitConfig({ ...initConfig, mode: mode });
});
};
useLayoutEffect(() => {
if (dom.current) {
window.addEventListener('resize', resize);
resize();
}
return () => {
window.removeEventListener('resize', resize);
};
}, []);
const query = queryString.parse(location.search) as {
id: string;
name: string;
type: string;
protocol: string;
};
const { type, id, name, protocol } = query as {
id: string;
name: string;
type: ConfigTabs;
protocol: AppProtocolType;
};
const items: MenuProps['items'] = [
{
key: ConfigTabs.basic,
label: intl.formatMessage({ id: 'pages.app.config.basic' }),
icon: React.createElement(() => {
return <ProfileOutlined />;
}),
},
{
key: ConfigTabs.login_access,
label: intl.formatMessage({ id: 'pages.app.config.items.login_access' }),
icon: React.createElement(() => {
return <DesktopOutlined />;
}),
children: [
{
key: ConfigTabs.protocol_config,
label: intl.formatMessage({ id: 'pages.app.config.items.login_access.protocol_config' }),
},
//OIDC 不展示应用账户
...(!protocol || protocol === AppProtocolType.oidc
? []
: [
{
key: ConfigTabs.app_account,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account',
}),
},
]),
{
key: ConfigTabs.access_policy,
label: intl.formatMessage({ id: 'pages.app.config.items.login_access.access_policy' }),
},
],
},
{
key: ConfigTabs.app_permission,
label: intl.formatMessage({ id: 'pages.app.config.items.app_permission' }),
icon: React.createElement(() => {
return <SafetyOutlined />;
}),
children: [
{
key: ConfigTabs.permission_resource,
label: intl.formatMessage({
id: 'pages.app.config.items.app_permission.permission_resource',
}),
},
{
key: ConfigTabs.permission_role,
label: intl.formatMessage({
id: 'pages.app.config.items.app_permission.permission_role',
}),
},
],
},
];
useAsyncEffect(async () => {
if (!id || !protocol) {
message.error(intl.formatMessage({ id: 'pages.app.config.error' }));
history.push('/app');
return;
}
if (!type || !ConfigTabs[type]) {
setKeys([ConfigTabs.basic]);
history.replace({
pathname: location.pathname,
search: queryString.stringify({
type: ConfigTabs.basic,
id,
protocol,
name,
}),
});
return;
}
setKeys([type]);
}, []);
useAsyncEffect(async () => {
setTitle(query.name);
}, [location]);
const ComponentByKey = ({ key, ...rest }: { key: string; appId: string; protocol: any }) => {
const components = {
[ConfigTabs.basic]: AppBasic,
[ConfigTabs.protocol_config]: AppProtocol,
[ConfigTabs.app_account]: AppAccount,
[ConfigTabs.access_policy]: AccessPolicy,
[ConfigTabs.permission_resource]: PermissionResource,
[ConfigTabs.permission_role]: PermissionRole,
};
const Component = components[key];
return <Component {...rest} />;
};
return wrapSSR(
<PageContainer
title={title}
style={{ overflow: 'hidden' }}
onBack={() => {
history.push('/app');
}}
>
<GridContent>
<div
className={classNames(`${prefixCls}-main`, hashId)}
ref={(ref) => {
if (ref) {
dom.current = ref;
}
}}
>
<div className={classNames(`${prefixCls}-left`, hashId)}>
<Menu
mode={initConfig.mode}
selectedKeys={keys}
className={classNames(`${prefixCls}-left-menu`, hashId)}
items={items}
onSelect={({ selectedKeys }) => {
setKeys(selectedKeys);
history.replace({
pathname: location.pathname,
search: queryString.stringify({ type: selectedKeys?.[0], id, protocol, name }),
});
}}
/>
</div>
<div className={classNames(`${prefixCls}-right`, hashId)}>
{keys && ComponentByKey({ key: keys?.[0] || '', appId: id, protocol: protocol })}
</div>
</div>
</GridContent>
</PageContainer>,
);
};

View File

@ -1,294 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getAllAppGroupList, updateApp } from '@/services/app';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useAsyncEffect } from 'ahooks';
import { App, Avatar, Skeleton, Upload } from 'antd';
import { omit } from 'lodash';
import React, { useState } from 'react';
import { history, useIntl, useLocation } from '@umijs/max';
import queryString from 'query-string';
import useStyle from './style';
import classNames from 'classnames';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { uploadFile } from '@/services/upload';
import { GetApp } from '@/pages/app/AppConfig/data';
import { getApp } from '../../service';
const prefixCls = 'app-basic-info';
const AppBasic = (props: { appId: string }) => {
const { appId } = props;
const intl = useIntl();
const useApp = App.useApp();
const [loading, setLoading] = useState<boolean>(true);
const [uploadLoading, setUploadLoading] = useState(false);
const [uploadIconUrl, setUploadIconUrl] = useState<string>();
const [app, setApp] = useState<GetApp>();
const location = useLocation();
const query = queryString.parse(location.search) as {
id: string;
name: string;
type: string;
protocol: string;
};
const { styles } = useStyle(prefixCls);
useAsyncEffect(async () => {
setLoading(true);
const { result, success } = await getApp(appId);
if (success && result) {
setApp(result);
}
setLoading(false);
}, [appId]);
/**
* onSave
*
* @param key
* @param record
*/
const onSave = async (key: React.Key | React.Key[], record: GetApp): Promise<any | void> => {
//调用接口修改
let params: Record<string, any> = {
id: record.id,
name: record.name,
remark: record.remark,
groupIds: record.groupIds,
};
if (uploadIconUrl) {
params = { ...params, icon: uploadIconUrl };
}
const { success } = await updateApp(params);
if (success) {
useApp.message.success(intl.formatMessage({ id: 'app.operation_success' }));
if (uploadIconUrl) {
setApp({ ...record, icon: uploadIconUrl });
} else {
setApp((values) => {
return { ...record, icon: values?.icon || '' };
});
}
history.replace(
`/app/config?id=${query.id}&name=${record.name}&protocol=${query.protocol}&type=${query.type}`,
);
return Promise.resolve(true);
}
return Promise.resolve(false);
};
return (
<ProCard
title={intl.formatMessage({ id: 'pages.app.config.basic' })}
headerBordered
className={styles.main}
>
<Skeleton loading={loading} active={true} paragraph={{ rows: 5 }}>
<div className={classNames(`${prefixCls}-descriptions`)}>
<ProDescriptions<GetApp>
size="small"
column={1}
dataSource={omit(app, 'config')}
editable={{
onSave: onSave,
}}
>
<ProDescriptions.Item
dataIndex="icon"
label={intl.formatMessage({ id: 'pages.app.config.basic.icon' })}
copyable={false}
fieldProps={{
preview: false,
width: 85,
}}
renderText={(text) => {
return (
<>
<Avatar
shape="square"
size={102}
src={text}
style={{ border: '1px dashed #d9d9d9' }}
/>
<div style={{ color: 'rgba(0, 0, 0, 0.45)', marginTop: '8px' }}>
<span>
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.1' })}
</span>
<br />
<span>
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.2' })}
</span>
</div>
</>
);
}}
renderFormItem={() => {
return (
<div>
<Upload
listType="picture-card"
name="file"
showUploadList={false}
accept="image/png, image/jpeg"
customRequest={async (files) => {
setUploadLoading(true);
if (!files.file) {
return;
}
const { success, result, message } = await uploadFile(files.file).finally(
() => {
setUploadLoading(false);
},
);
if (success && result) {
setUploadIconUrl(result);
return;
}
useApp.message.error(message);
}}
>
{uploadIconUrl ? (
<img src={uploadIconUrl} alt="avatar" style={{ width: '100%' }} />
) : (
<div>
{uploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}></div>
</div>
)}
</Upload>
<div style={{ color: 'rgba(0, 0, 0, 0.45)' }}>
<span>
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.1' })}
</span>
<br />
<span>
{intl.formatMessage({ id: 'pages.app.config.basic.icon.desc.2' })}
</span>
</div>
</div>
);
}}
/>
</ProDescriptions>
<ProDescriptions<GetApp>
size="small"
column={2}
dataSource={omit(app, 'config')}
editable={{
onSave: onSave,
}}
>
<ProDescriptions.Item
dataIndex="name"
label={intl.formatMessage({ id: 'pages.app.config.basic.name' })}
copyable={false}
formItemProps={{
rules: [
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.basic.name.rule.0.message',
}),
},
],
}}
/>
<ProDescriptions.Item
dataIndex="groupIds"
label={intl.formatMessage({ id: 'pages.app.config.basic.group' })}
valueType={'select'}
request={async () => {
const { success, data } = await getAllAppGroupList({}, {}, {});
if (success && data) {
return data.map((i) => {
return { label: i.name, value: i.id };
});
}
return [];
}}
fieldProps={{ rows: 2, maxLength: 20, maxTagCount: 'responsive', mode: 'multiple' }}
copyable={false}
/>
<ProDescriptions.Item
dataIndex="enabled"
label={intl.formatMessage({ id: 'pages.app.config.basic.enabled' })}
editable={false}
valueEnum={{
true: { text: intl.formatMessage({ id: 'app.normal' }), status: 'Success' },
false: { text: intl.formatMessage({ id: 'app.disable' }), status: 'Error' },
}}
/>
<ProDescriptions.Item
dataIndex="type"
label={intl.formatMessage({ id: 'pages.app.config.basic.type' })}
editable={false}
valueEnum={{
custom_made: {
text: intl.formatMessage({
id: 'pages.app.config.basic.type.value_enum.custom_made',
}),
},
standard: {
text: intl.formatMessage({
id: 'pages.app.config.basic.type.value_enum.standard',
}),
},
self_developed: {
text: intl.formatMessage({
id: 'pages.app.config.basic.type.value_enum.self_developed',
}),
},
}}
/>
<ProDescriptions.Item
dataIndex="clientId"
ellipsis
label={intl.formatMessage({ id: 'pages.app.config.basic.client_id' })}
valueType={'text'}
editable={false}
copyable={true}
/>
<ProDescriptions.Item
dataIndex="clientSecret"
label={intl.formatMessage({ id: 'pages.app.config.basic.client_secret' })}
valueType={'password'}
editable={false}
copyable={true}
/>
<ProDescriptions.Item
dataIndex="createTime"
label={intl.formatMessage({ id: 'pages.app.config.basic.create_time' })}
valueType={'dateTime'}
copyable={false}
editable={false}
/>
<ProDescriptions.Item
dataIndex="remark"
label={intl.formatMessage({ id: 'pages.app.config.basic.remark' })}
valueType={'textarea'}
fieldProps={{ rows: 2, maxLength: 200 }}
copyable={false}
/>
</ProDescriptions>
</div>
</Skeleton>
</ProCard>
);
};
export default AppBasic;

View File

@ -1,46 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { createStyles } from 'antd-style';
const useStyle = createStyles(({ prefixCls, token }, props) => {
const antCls = `.${prefixCls}`;
const prefix = `${props}`;
return {
main: {
height: 'calc(100vh - 178px)',
[`${antCls}-pro-card-body`]: {
overflow: 'auto !important',
},
[`.${prefix}-descriptions`]: {
[`${antCls}-descriptions-item-container ${antCls}-space-item`]: {
span: {
padding: '0 !important',
},
},
[`${antCls}-descriptions-small ${antCls}-descriptions-row > th, ${antCls}-descriptions-small ${antCls}-descriptions-row > td`]:
{
paddingBottom: '16px',
},
},
[`@media screen and (max-width: ${token.screenXL}px)`]: {
height: 'calc(100vh - 240px)',
},
},
};
});
export default useStyle;

View File

@ -1,401 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getAppConfig, saveAppConfig } from '@/services/app';
import { useAsyncEffect } from 'ahooks';
import { Alert, App, Divider, Form, Spin } from 'antd';
import React, { useState } from 'react';
import {
EditableProTable,
FooterToolbar,
ProColumns,
ProForm,
ProFormDependency,
ProFormRadio,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
import { FormEncryptType } from '../../../constant';
import { omit } from 'lodash';
import ConfigAbout from './ConfigAbout';
import { useIntl } from '@umijs/max';
import { AuthorizationType } from '../CommonConfig';
import { GetApp } from '../../../data.d';
import { generateUUID } from '@/utils/utils';
const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 12,
},
};
export default (props: { app: GetApp | Record<string, any> }) => {
const { app } = props;
const intl = useIntl();
const { message } = App.useApp();
const { id, template } = app;
const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(true);
const [otherFieldTableForm] = Form.useForm();
const [otherFieldEditableKeys, setOtherFieldEditableKeys] = useState<React.Key[]>(() => []);
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
const getConfig = async () => {
setLoading(true);
const { result, success } = await getAppConfig(id);
if (success && result) {
form.setFieldsValue({ ...omit(result, 'protocolEndpoint', 'otherField'), appId: id });
//设置Endpoint相关
setProtocolEndpoint(result.protocolEndpoint);
//其他字段
if (result?.otherField) {
const otherField = result?.otherField.map((i: Record<string, string>) => {
return { key: generateUUID(), fieldValue: i.fieldValue, fieldName: i.fieldName };
});
form.setFieldsValue({ otherField: otherField });
setOtherFieldEditableKeys(
otherField.map((i: Record<string, string>) => {
return i.key;
}),
);
}
}
setLoading(false);
};
useAsyncEffect(async () => {
await getConfig();
}, []);
return (
<Spin spinning={loading}>
<Alert
showIcon={true}
message={
<span>
{intl.formatMessage({ id: 'app.issue' })}
{intl.formatMessage({ id: 'app.disposition' })}{' '}
<a
target={'_blank'}
href={'https://eiam.topiam.cn/docs/application/form/overview'}
rel="noreferrer"
>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form',
})}
</a>{' '}
</span>
}
/>
<br />
<ProForm
form={form}
requiredMark={true}
layout={'horizontal'}
{...formItemLayout}
scrollToFirstError
onFinish={async (values) => {
const validate = await otherFieldTableForm.validateFields();
setLoading(true);
if (validate) {
const { success } = await saveAppConfig({
id,
template,
config: omit(values, 'id', 'template'),
}).finally(() => {
setLoading(false);
});
if (success) {
message.success(intl.formatMessage({ id: 'app.save_success' }));
await getConfig();
return true;
}
message.error(intl.formatMessage({ id: 'app.save_fail' }));
}
return false;
}}
submitter={{
render: (_, dom) => {
return <FooterToolbar>{dom}</FooterToolbar>;
},
}}
>
<ProFormText name={'appId'} hidden />
<ProFormText
name={'loginUrl'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.login_url',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.login_url.rule.1.message',
}),
},
]}
/>
<ProFormText
name={'usernameField'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_field',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_field.rule.0.message',
}),
},
]}
/>
<ProFormText
name={'passwordField'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_field',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_field.rule.0.message',
}),
},
]}
/>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type',
})}
name={'passwordEncryptType'}
options={[
{ value: FormEncryptType.aes, label: 'AES' },
{ value: FormEncryptType.base64, label: 'BASE64' },
{ value: FormEncryptType.md5, label: 'MD5' },
]}
fieldProps={{ allowClear: true }}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type.extra',
})}
/>
<ProFormDependency name={['passwordEncryptType']}>
{({ passwordEncryptType }) => {
return passwordEncryptType === FormEncryptType.aes ? (
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key',
})}
name={'passwordEncryptKey'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message',
}),
},
]}
/>
) : (
<></>
);
}}
</ProFormDependency>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type',
})}
name={'usernameEncryptType'}
options={[
{ value: FormEncryptType.aes, label: 'AES' },
{ value: FormEncryptType.base64, label: 'BASE64' },
]}
fieldProps={{ allowClear: true }}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type.extra',
})}
/>
<ProFormDependency name={['usernameEncryptType']}>
{({ usernameEncryptType }) => {
return usernameEncryptType === FormEncryptType.aes ? (
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key',
})}
name={'usernameEncryptKey'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message',
}),
},
]}
/>
) : (
<></>
);
}}
</ProFormDependency>
<ProFormRadio.Group
name={'submitType'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.submit_type',
})}
initialValue={['post']}
options={[
{ value: 'post', label: 'POST' },
{ value: 'get', label: 'GET' },
]}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.submit_type.rule.0.message',
}),
},
]}
/>
<Divider />
{/*授权类型*/}
<AuthorizationType />
<Divider />
<ProForm.Item
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field',
})}
name="otherField"
trigger="onValuesChange"
>
<EditableProTable
rowKey={'key'}
toolBarRender={false}
scroll={{
x: 500,
}}
columns={
[
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name',
}),
dataIndex: 'fieldName',
fieldProps: {
allowClear: false,
},
formItemProps: {
rules: [
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0',
}),
},
],
},
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value',
}),
key: 'fieldValue',
dataIndex: 'fieldValue',
ellipsis: true,
formItemProps: {
rules: [
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0',
}),
},
],
},
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.columns.option',
}),
valueType: 'option',
align: 'center',
fixed: 'right',
width: 50,
},
] as ProColumns[]
}
recordCreatorProps={{
creatorButtonText: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.record_creator_props',
}),
newRecordType: 'dataSource',
position: 'bottom',
record: () => ({
key: Date.now(),
}),
}}
editable={{
form: otherFieldTableForm,
type: 'multiple',
editableKeys: otherFieldEditableKeys,
onChange: setOtherFieldEditableKeys,
deletePopconfirmMessage: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.other_field.editable',
}),
actionRender: (row, _, dom) => {
return [dom.delete];
},
}}
/>
</ProForm.Item>
</ProForm>
<Divider style={{ margin: 0 }} />
<ConfigAbout appId={id} protocolEndpoint={protocolEndpoint} collapsed={true} />
</Spin>
);
};

View File

@ -1,245 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getAppConfig, saveAppConfig } from '@/services/app';
import { useAsyncEffect } from 'ahooks';
import { Alert, App, Divider, Form, Spin } from 'antd';
import React, { useState } from 'react';
import {
FooterToolbar,
ProForm,
ProFormDigit,
ProFormRadio,
ProFormSelect,
ProFormText,
} from '@ant-design/pro-components';
import ConfigAbout from './ConfigAbout';
import { omit } from 'lodash';
import { useIntl } from '@umijs/max';
import { AuthorizationType } from '../CommonConfig';
import { GetApp } from '../../../data.d';
const layout = {
labelCol: {
xs: { span: 24 },
sm: { span: 7 },
md: { span: 6 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 13 },
md: { span: 14 },
},
};
export default (props: { app: GetApp | Record<string, any> }) => {
const { app } = props;
const { id, template } = app;
const [form] = Form.useForm();
const [loading, setLoading] = useState<boolean>(true);
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
const intl = useIntl();
const { message } = App.useApp();
const getConfig = async () => {
setLoading(true);
const { result, success } = await getAppConfig(id);
if (success && result) {
form.setFieldsValue({ ...omit(result, 'protocolEndpoint'), appId: id });
//设置Endpoint相关
setProtocolEndpoint(result.protocolEndpoint);
}
setLoading(false);
};
useAsyncEffect(async () => {
await getConfig();
}, []);
return (
<Spin spinning={loading}>
<Alert
showIcon={true}
message={
<span>
{intl.formatMessage({ id: 'app.issue' })}
{intl.formatMessage({ id: 'app.disposition' })}{' '}
<a
target={'_blank'}
href={'https://eiam.topiam.cn/docs/application/jwt/overview'}
rel="noreferrer"
>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt',
})}
</a>{' '}
</span>
}
/>
<br />
<ProForm
layout={'horizontal'}
{...layout}
form={form}
scrollToFirstError
onFinish={async (values) => {
setLoading(true);
const { success } = await saveAppConfig({
id,
template,
config: omit(values, 'id', 'template'),
}).finally(() => {
setLoading(false);
});
if (success) {
message.success(intl.formatMessage({ id: 'app.save_success' }));
await getConfig();
return true;
}
message.error(intl.formatMessage({ id: 'app.save_fail' }));
return false;
}}
submitter={{
render: (_, dom) => {
return <FooterToolbar>{dom}</FooterToolbar>;
},
}}
>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url',
})}
name={'redirectUrl'}
fieldProps={{ allowClear: false }}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.1.message',
}),
},
]}
/>
<ProFormText
label={'target link url'}
name={'targetLinkUrl'}
fieldProps={{ allowClear: false }}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.extra',
})}
rules={[
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.rule.0.message',
}),
},
]}
/>
<ProFormRadio.Group
name={'bindingType'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type',
})}
initialValue={['post']}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type.extra',
})}
options={[
{ value: 'post', label: 'POST' },
{ value: 'get', label: 'GET' },
]}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.binding_type.rule.0.message',
}),
},
]}
/>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type',
})}
name={'idTokenSubjectType'}
options={[
{
value: 'user_id',
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0',
}),
},
{
value: 'app_user',
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1',
}),
},
]}
fieldProps={{ allowClear: false }}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message',
}),
},
]}
/>
<ProFormDigit
name={'idTokenTimeToLive'}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live',
})}
addonAfter={'秒'}
max={84600}
min={1}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.rule.0.message',
}),
},
]}
/>
<Divider />
{/*授权类型*/}
<AuthorizationType />
</ProForm>
<Divider style={{ margin: 0 }} />
<ConfigAbout appId={app.id} protocolEndpoint={protocolEndpoint} collapsed={true} />
</Spin>
);
};

View File

@ -1,637 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { SsoInitiator } from '@/pages/app/AppConfig/constant';
import { getAppConfig, saveAppConfig } from '@/services/app';
import {
FooterToolbar,
ProForm,
ProFormCheckbox,
ProFormDependency,
ProFormDigit,
ProFormSelect,
ProFormSwitch,
ProFormText,
} from '@ant-design/pro-components';
import { useMount } from 'ahooks';
import { Alert, App, Button, Collapse, Divider, Form, Input, Spin } from 'antd';
import React, { useState } from 'react';
import ConfigAbout from './ConfigAbout';
import { omit } from 'lodash';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
import { useIntl } from '@umijs/max';
import { AuthorizationType } from '../CommonConfig';
import { createStyles } from 'antd-style';
import { GetApp } from '../../../data.d';
const formItemLayout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 12,
},
};
const formItemLayoutWithOutLabel = {
wrapperCol: {
span: 12,
offset: 6,
},
};
const useStyle = createStyles(({ prefixCls, css }) => {
return css`
.${prefixCls}-checkbox-group-item {
align-items: flex-start;
}
.${prefixCls}-checkbox {
align-self: flex-start;
top: 2px;
}
`;
});
export default (props: { app: GetApp | Record<string, any> }) => {
const intl = useIntl();
const { styles } = useStyle();
const { message } = App.useApp();
const { app } = props;
const { id, template } = app;
const [form] = Form.useForm();
const [protocolEndpoint, setProtocolEndpoint] = useState<Record<string, string>>({});
const [loading, setLoading] = useState<boolean>(true);
useMount(async () => {
setLoading(true);
const { result, success } = await getAppConfig(id);
if (success && result) {
form.setFieldsValue({
appId: id,
...result,
});
//设置Endpoint相关
setProtocolEndpoint(result.protocolEndpoint);
}
setLoading(false);
});
return (
<Spin spinning={loading}>
<Alert
showIcon={true}
message={
<span>
{intl.formatMessage({ id: 'app.issue' })}
{intl.formatMessage({ id: 'app.disposition' })}{' '}
<a
target={'_blank'}
href={'https://eiam.topiam.cn/docs/application/oidc/overview'}
rel="noreferrer"
>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc',
})}
</a>{' '}
</span>
}
/>
<br />
<ProForm
className={styles}
requiredMark={true}
layout={'horizontal'}
{...formItemLayout}
form={form}
scrollToFirstError
onFinish={async (values) => {
const { success } = await saveAppConfig({
id,
template,
config: omit(values, 'id', 'template'),
});
if (success) {
message.success(intl.formatMessage({ id: 'app.operation_success' }));
return true;
}
message.success(intl.formatMessage({ id: 'app.operation_fail' }));
return false;
}}
submitter={{
render: (_, dom) => {
return <FooterToolbar>{dom}</FooterToolbar>;
},
}}
>
<ProFormText name={'appId'} hidden />
<ProFormCheckbox.Group
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types',
})}
layout={'vertical'}
name={'authGrantTypes'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.rule.0.message',
}),
},
]}
options={[
{
value: 'authorization_code',
label: (
<>
<span style={{ marginRight: '12px' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.1',
})}
</span>
</>
),
},
{
value: 'refresh_token',
label: (
<>
<span style={{ marginRight: '12px' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.1',
})}
</span>
</>
),
},
{
value: 'implicit',
label: (
<>
<span style={{ marginRight: '12px' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.1',
})}
</span>
</>
),
},
{
value: 'password',
label: (
<>
<span style={{ marginRight: '12px' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.1',
})}
</span>
</>
),
},
]}
/>
<ProFormDependency name={['authGrantTypes']}>
{({ authGrantTypes }) => {
return (
authGrantTypes?.includes('authorization_code') && (
<ProFormSwitch
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key',
})}
name={'requireProofKey'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key.extra',
})}
/>
)
);
}}
</ProFormDependency>
<Form.List
name="redirectUris"
rules={[
{
validator: async (_, value) => {
if (value && value.length > 0) {
return null;
}
return Promise.reject(
new Error(
intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
}),
),
);
},
},
]}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
required={true}
key={field.key}
label={
index === 0
? intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris',
})
: ''
}
>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message',
}),
},
]}
noStyle
>
<Input
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.placeholder',
})}
/>
</Form.Item>
<DeleteOutlined onClick={() => remove(field.name)} />
</div>
</Form.Item>
))}
<Form.Item
{...(fields.length === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
required={true}
label={
fields.length === 0
? intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris',
})
: ''
}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.extra',
})}
>
<Button
type="dashed"
onClick={() => add()}
icon={<PlusOutlined />}
style={{ width: '100%' }}
>
{intl.formatMessage({ id: 'app.add' })}
</Button>
<Form.ErrorList errors={errors} />
</Form.Item>
</>
)}
</Form.List>
<Form.List name="postLogoutRedirectUris">
{(fields, { add, remove }, {}) => (
<>
{fields.map((field, index) => {
return (
<Form.Item
{...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
key={field.key}
label={
index === 0
? intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
})
: ''
}
>
<div
style={{
display: 'flex',
alignItems: 'center',
gap: '8px',
}}
>
<Form.Item
{...field}
validateTrigger={['onChange', 'onBlur']}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message',
}),
},
]}
noStyle
>
<Input
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.placeholder',
})}
/>
</Form.Item>
<DeleteOutlined onClick={() => remove(field.name)} />
</div>
</Form.Item>
);
})}
<Form.Item
{...(fields.length === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
label={
fields.length === 0
? intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
})
: ''
}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.extra',
})}
>
<Button
type="dashed"
onClick={() => add()}
icon={<PlusOutlined />}
style={{ width: '100%' }}
>
{intl.formatMessage({ id: 'app.add' })}
</Button>
</Form.Item>
</>
)}
</Form.List>
{/*授权类型*/}
<AuthorizationType />
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type',
})}
name={'initLoginType'}
allowClear={false}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.rule.0.message',
}),
},
]}
options={[
{
value: SsoInitiator.APP,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.0',
}),
},
{
value: SsoInitiator.PORTAL_OR_APP,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.1',
}),
},
]}
extra={
<>
<span>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.0',
})}
</span>
<br />
<span>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.1',
})}
</span>
</>
}
/>
<ProFormDependency name={['initLoginType']}>
{(values) => {
return (
values?.initLoginType === SsoInitiator.PORTAL_OR_APP && (
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url',
})}
name={'initLoginUrl'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.rule.0.message',
}),
},
]}
fieldProps={{
placeholder: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.field_props',
}),
}}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.extra',
})}
/>
)
);
}}
</ProFormDependency>
<Divider style={{ margin: 0 }} />
<Collapse
ghost
expandIconPosition={'start'}
defaultActiveKey={'advanced'}
items={[
{
key: 'advanced',
label: (
<a>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced',
})}
</a>
),
children: (
<>
<ProFormCheckbox.Group
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes',
})}
layout={'vertical'}
name={'grantScopes'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message',
}),
},
]}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.extra',
})}
options={[
{
value: 'openid',
label: 'openid',
disabled: true,
},
{
value: 'email',
label: (
<>
<span style={{ marginRight: '12px' }}>email</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.1.label',
})}
</span>
</>
),
},
{
value: 'phone',
label: (
<>
<span style={{ marginRight: '12px' }}>phone</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.2.label',
})}
</span>
</>
),
},
{
value: 'profile',
label: (
<>
<span style={{ marginRight: '12px' }}>profile</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label',
})}
</span>
</>
),
},
]}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live',
})}
name={'accessTokenTimeToLive'}
addonAfter={intl.formatMessage({ id: 'app.minute' })}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra',
})}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live',
})}
name={'refreshTokenTimeToLive'}
addonAfter={intl.formatMessage({ id: 'app.minute' })}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra',
})}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live',
})}
readonly
name={'idTokenTimeToLive'}
addonAfter={intl.formatMessage({ id: 'app.minute.not_update' })}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live.extra',
})}
/>
<ProFormSelect
options={[
{ value: 'ES256', label: 'ES256' },
{ value: 'RS256', label: 'RS256' },
]}
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm',
})}
name={'idTokenSignatureAlgorithm'}
allowClear={false}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message',
}),
},
]}
/>
</>
),
},
]}
/>
</ProForm>
<Divider style={{ margin: 0 }} />
<ConfigAbout appId={id} protocolEndpoint={protocolEndpoint} collapsed={true} />
</Spin>
);
};

View File

@ -1,152 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getPermissionResource, permissionResourceParamCheck } from '../../service';
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
import { Form, Spin } from 'antd';
import React, { useState } from 'react';
import { useAsyncEffect } from 'ahooks';
import Paragraph from 'antd/es/typography/Paragraph';
type UpdateFormProps = {
/**
* ID
*/
id?: string;
/**
* SYSTEM
*/
appId: string;
/**
*
*/
open: boolean;
/**
*
*/
onCancel: (e?: React.MouseEvent | React.KeyboardEvent) => void;
/**
*
*/
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
};
const UpdateResource: React.FC<UpdateFormProps> = (props) => {
const { open, onCancel, onFinish, id, appId } = props;
const [loading, setLoading] = useState<boolean>(false);
const [form] = Form.useForm();
useAsyncEffect(async () => {
if (open && id) {
setLoading(true);
const { success, result } = await getPermissionResource(id);
if (success && result) {
setLoading(false);
return;
}
}
}, [id, onCancel, open]);
return (
<ModalForm
title="修改资源"
width={'500px'}
open={open}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
layout={'horizontal'}
labelAlign={'right'}
modalProps={{
maskClosable: true,
destroyOnClose: true,
onCancel: onCancel,
}}
form={form}
onFinish={async (values) => {
setLoading(true);
const result = await onFinish?.(values);
setLoading(false);
return result;
}}
>
<Spin spinning={loading}>
<ProFormText name={'id'} hidden />
<ProFormText
name="name"
label="资源名称"
placeholder="请输入资源名称"
rules={[
{
required: true,
message: '请输入资源名称',
},
{
validator: async (rule, value) => {
if (!value) {
return Promise.resolve();
}
setLoading(true);
const { success, result } = await permissionResourceParamCheck(
appId,
'NAME',
value,
id,
);
setLoading(false);
if (!success) {
return Promise.reject<any>();
}
if (!result) {
return Promise.reject<any>(new Error('资源名称已存在'));
}
},
validateTrigger: ['onBlur'],
},
]}
/>
<ProFormText
name="code"
label="资源编码"
placeholder="请输入资源编码"
proFieldProps={{
render: (value: string) => {
return (
value && (
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
<span
dangerouslySetInnerHTML={{
__html: `<span>${value}</span>`,
}}
/>
</Paragraph>
)
);
},
}}
readonly
extra="资源编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
/>
<ProFormTextArea
name="desc"
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
label="资源描述"
placeholder="请输入资源描述"
/>
</Spin>
</ModalForm>
);
};
export default UpdateResource;

View File

@ -1,149 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getPermissionRole, permissionRoleParamCheck } from '../../service';
import type { ProFormInstance } from '@ant-design/pro-components';
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
import { Spin } from 'antd';
import React, { useRef, useState } from 'react';
import { useAsyncEffect } from 'ahooks';
import Paragraph from 'antd/es/typography/Paragraph';
type UpdateFormProps = {
/**
* ID
*/
id: string | undefined;
/**
*
*/
open: boolean;
/**
*
*/
onCancel: () => void;
/**
*
*/
onFinish?: (formData: Record<string, string>) => Promise<boolean | void>;
};
const UpdateRole: React.FC<UpdateFormProps> = (props) => {
const { open, onCancel, onFinish, id } = props;
const [loading, setLoading] = useState<boolean>(false);
const formRef = useRef<ProFormInstance>();
useAsyncEffect(async () => {
if (open && id) {
setLoading(true);
const { success, result } = await getPermissionRole(id);
if (success && result) {
setLoading(false);
return;
}
}
}, [id, open]);
return (
<ModalForm
title="修改角色"
width={600}
modalProps={{
onCancel: onCancel,
destroyOnClose: true,
}}
layout={'horizontal'}
labelCol={{ span: 4 }}
wrapperCol={{ span: 19 }}
labelAlign={'right'}
open={open}
key={'update'}
onFinish={async (values) => {
setLoading(true);
const result = await onFinish?.(values);
setLoading(false);
return result;
}}
>
<Spin spinning={loading}>
<ProFormText name="id" hidden />
<ProFormText name="appId" hidden />
<ProFormText
name="name"
label="名称"
rules={[
{
required: true,
message: '请输入角色名称',
},
{
validator: async (rule, value) => {
if (!value) {
return Promise.resolve();
}
setLoading(true);
const { success, result } = await permissionRoleParamCheck(
formRef.current?.getFieldValue('appId'),
'NAME',
value,
id,
);
setLoading(false);
if (!success) {
return Promise.reject<any>();
}
if (!result) {
return Promise.reject<any>(new Error('手机号已存在'));
}
},
validateTrigger: ['onBlur'],
},
]}
placeholder="请输入角色名称"
/>
<ProFormText
name="code"
label="标识"
placeholder="请输入角色标识"
readonly
proFieldProps={{
render: (value: string) => {
return (
value && (
<Paragraph copyable={{ text: value }} style={{ marginBottom: '0' }}>
<span
dangerouslySetInnerHTML={{
__html: `<span>${value}</span>`,
}}
/>
</Paragraph>
)
);
},
}}
extra="角色编码在当前应用中的唯一标识,不能重复,仅支持英文、数字、下划线,创建后不可修改。"
/>
<ProFormTextArea
name="remark"
fieldProps={{ rows: 2, maxLength: 20, showCount: false }}
label="描述"
placeholder="请输入角色描述"
/>
</Spin>
</ModalForm>
);
};
export default UpdateRole;

View File

@ -1,603 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
export default {
'pages.app.config.basic': '基本信息',
'pages.app.config.basic.name': '应用名称',
'pages.app.config.basic.name.rule.0.message': '应用名称为必填项',
'pages.app.config.basic.icon': '应用图标',
'pages.app.config.basic.icon.rule.0.message': '应用图标为必填项',
'pages.app.config.basic.icon.rule.1.message': '应用图标上传失败',
'pages.app.config.basic.icon.desc.1': '必须为 PNG/JPG 格式',
'pages.app.config.basic.icon.desc.2': '建议使用 256 * 256 像素方形图标',
'pages.app.config.basic.enabled': '应用状态',
'pages.app.config.basic.type': '应用类型',
'pages.app.config.basic.group': '应用分组',
'pages.app.config.basic.type.value_enum.custom_made': '定制应用',
'pages.app.config.basic.type.value_enum.standard': '标准应用',
'pages.app.config.basic.type.value_enum.self_developed': '自研应用',
'pages.app.config.basic.protocol_name': '应用协议',
'pages.app.config.basic.client_id': '客户端 ID',
'pages.app.config.basic.client_secret': '客户端秘钥',
'pages.app.config.basic.create_time': '创建时间',
'pages.app.config.basic.remark': '备注',
'pages.app.config.items.login_access': '登录访问',
'pages.app.config.items.login_access.protocol_config': '单点登录',
'pages.app.config.items.login_access.protocol_config.common.authorization_type': '授权范围',
'pages.app.config.items.login_access.protocol_config.common.authorization_type.extra':
'若选择手动授权,需要在 访问授权 中进行权限分配。',
'pages.app.config.items.login_access.protocol_config.common.authorization_type.rule.0.message':
'请选择授权范围',
'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.0':
'手动授权',
'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.1':
'全员可访问',
'pages.app.config.items.login_access.protocol_config.cas.client_service_url': 'CAS 客户端的地址',
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.extra':
'单点登录时,将选中项作为 CAS 用户标识,传递给业务系统。',
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.rule.0.message':
'请输入 CAS 客户端的地址',
'pages.app.config.items.login_access.protocol_config.cas.client_service_url.rule.1.message':
'CAS 客户端地址格式不正确',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type': 'CAS 用户标识',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.0': '用户名称',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.1': '用户姓名',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.2': '用户昵称',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.3': '邮箱地址',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.option.4': '应用账户',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.extra':
'单点登录时,将选中项作为 CAS 用户标识,传递给业务系统。',
'pages.app.config.items.login_access.protocol_config.cas.user_identity_type.rule.0.message':
'请选择 CAS 用户标识',
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time':
'ST 过期时间',
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time.extra':
'ServiceTicket 过期时间',
'pages.app.config.items.login_access.protocol_config.cas.service_ticket_expire_time.rule.0.message':
'请配置 ServiceTicket 过期时间',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type': 'SSO 发起方',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.rule.0.message':
'请配置 SSO 发起方',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.option.0':
'只允许应用发起',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.option.1':
'支持门户和应用发起',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.extra.0':
'门户发起:由 IAM 门户页点击进行 SSO 。',
'pages.app.config.items.login_access.protocol_config.cas.init_login_type.extra.1':
'应用发起:由应用登录主动发起。',
'pages.app.config.items.login_access.protocol_config.cas.init_login_url': '登录发起地址',
'pages.app.config.items.login_access.protocol_config.cas.init_login_url.placeholder':
'请输入登录链接',
'pages.app.config.items.login_access.protocol_config.cas.init_login_url.extra':
'若您希望由 TopIAM 门户页访问应用,请填写 TopIAM 发起 SSO 请求访问的应用地址。该地址接收到请求,应即刻转向 TopIAM / login 授权端点。 ',
'pages.app.config.items.login_access.protocol_config.cas.config_about': '应用配置信息',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_server_url_prefix':
'CAS 服务地址前缀',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_sso_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_slo_endpoint':
'登出端点',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_slo_endpoint.extra':
'应用发起单点登出的地址。',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_endpoint':
'service ticket 检验端点CAS 1.0',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_v2_endpoint':
'service ticket 检验端点CAS 2.0',
'pages.app.config.items.login_access.protocol_config.cas.config_about.cas_validate_v3_endpoint':
'service ticket 检验端点CAS 3.0',
'pages.app.config.items.login_access.protocol_config.form': '表单代填单点登录',
'pages.app.config.items.login_access.protocol_config.form.login_url': '登录提交URL',
'pages.app.config.items.login_access.protocol_config.form.login_url.placeholder':
'请输入登录提交URL',
'pages.app.config.items.login_access.protocol_config.form.login_url.extra':
'登录表单提交完整URL以http://或https://开头https://oa.xxxx.com/login',
'pages.app.config.items.login_access.protocol_config.form.login_url.rule.0.message':
'应用登录URL为必填项',
'pages.app.config.items.login_access.protocol_config.form.login_url.rule.1.message':
'URL格式不正确',
'pages.app.config.items.login_access.protocol_config.form.username_field': '登录名属性名称',
'pages.app.config.items.login_access.protocol_config.form.username_field.placeholder':
'请输入登录名属性名称',
'pages.app.config.items.login_access.protocol_config.form.username_field.extra':
'username标签的name属性',
'pages.app.config.items.login_access.protocol_config.form.username_field.rule.0.message':
'登录名属性名称为必填项',
'pages.app.config.items.login_access.protocol_config.form.password_field': '登录密码属性名称',
'pages.app.config.items.login_access.protocol_config.form.password_field.placeholder':
'请输入登录密码属性名称',
'pages.app.config.items.login_access.protocol_config.form.password_field.extra':
'password标签的name属性',
'pages.app.config.items.login_access.protocol_config.form.password_field.rule.0.message':
'登录密码属性名称为必填项',
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type':
'登录密码加密算法',
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_type.extra':
'登录密码加密算法类型。',
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key':
'登录密码加密秘钥',
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.extra':
'登录密码加密秘钥。',
'pages.app.config.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message':
'登录密码加密秘钥为必填项',
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type':
'用户名加密算法',
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_type.extra':
'用户名加密算法类型。',
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key': '用户名加密秘钥',
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.extra':
'用户名加密秘钥。',
'pages.app.config.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message':
'用户名加密秘钥为必填项',
'pages.app.config.items.login_access.protocol_config.form.submit_type': '登录提交方式',
'pages.app.config.items.login_access.protocol_config.form.submit_type.rule.0.message':
'登录提交方式为必选项',
'pages.app.config.items.login_access.protocol_config.form.other_field': '登录其他字段',
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name':
'属性名称',
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0':
'此项为必填项',
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value':
'属性值',
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0':
'此项为必填项',
'pages.app.config.items.login_access.protocol_config.form.other_field.columns.option': '操作',
'pages.app.config.items.login_access.protocol_config.form.other_field.record_creator_props':
'添加其他字段',
'pages.app.config.items.login_access.protocol_config.form.other_field.editable':
'您确定要删除此属性吗?',
'pages.app.config.items.login_access.protocol_config.form.config_about': '应用配置信息',
'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.items.login_access.protocol_config.jwt': 'JWT单点登录',
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url': 'SSO 地址',
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.extra':
'业务系统中的JWT SSO地址在单点登录时本系统将向该地址发送id_token信息参数名为id_token业务系统通过id_token与Public Key可获取业务系统中的用户信息如果在业务系统SP发起登录请求SP登录地址时如果携带redirect_uri参数系统会检验合法性成功后会将浏览器重定向到该地址并携带id_token身份令牌。',
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.0.message':
'请输入JWT应用SSO地址',
'pages.app.config.items.login_access.protocol_config.jwt.redirect_url.rule.1.message':
'JWT应用SSO地址格式不正确',
'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.extra':
'业务系统中在JWT SSO成功后重定向的URL一般用于跳转到二级菜单等若设置了该URL在JWT SSO时会以参数target_link_url优先传递该值若未设置该值此时若SSO中有请求参数target_link_url则会按照请求参数传递该值。此项可选。',
'pages.app.config.items.login_access.protocol_config.jwt.target_link_url.rule.0.message':
'地址格式不正确',
'pages.app.config.items.login_access.protocol_config.jwt.binding_type': 'SSO 绑定类型',
'pages.app.config.items.login_access.protocol_config.jwt.binding_type.extra':
'指定向JWT应用发送 id_token 的请求方式。',
'pages.app.config.items.login_access.protocol_config.jwt.binding_type.rule.0.message':
'登录提交方式为必选项',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type':
'id_token 主体类型',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0': '用户ID',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1':
'应用账户',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.extra':
'id_token 中 sub 主体(用户) 类型',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message':
'请选择 CAS 用户标识',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live':
'id_token 过期时间',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra':
'id_token 的有效期单位为秒。可设置范围为1-84600。',
'pages.app.config.items.login_access.protocol_config.jwt.idtoken_time_to_live.rule.0.message':
'请配置 id_token 的有效期',
'pages.app.config.items.login_access.protocol_config.jwt.config_about': '应用配置信息',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint':
'登出端点',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint.extra':
'应用发起单点登出的地址。',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert':
'JWT 验签公钥',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.0':
'下载或复制证书,并导入或粘贴到应用中。',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.1':
'复制证书内容',
'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.2':
'下载证书 .cer 文件',
'pages.app.config.items.login_access.protocol_config.oidc': 'OIDC 单点登录',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types': '授权模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.rule.0.message':
'请勾选授权模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.0':
'授权码模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.1':
' authorization_code 模式,用于账户的登录认证、授权。',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.0':
'令牌刷新模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.1':
'refresh_token 模式,用于既有 token 的延期。',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0':
'隐式模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.1':
' implicit\n' +
' 模式,由于协议本身的安全性,通常不推荐使用。如果有特殊需求,可以使用变体的' +
' PKCE 的授权码模式。',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0':
'密码模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.1':
'password 模式,由于协议本身的安全性,通常不推荐使用。',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.0':
'设备模式',
'pages.app.config.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.1':
'device 模式,兼容设备发起的登录流程。',
'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key':
'PKCE用于授权码模式',
'pages.app.config.items.login_access.protocol_config.oidc.require_proof_key.extra':
'PKCE Proof Key for Code Exchange是 OAuth 2.0 的安全性扩展模式,用于防护 CSRF、中间人进攻等恶意攻击。',
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris': '登录 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.extra':
'Redirect URI 白名单,应用在请求登录时携带 redirect_uri 参数该值需要在白名单中IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message':
'请配置登录 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message':
'Redirect URI 格式不正确',
'pages.app.config.items.login_access.protocol_config.oidc.redirect_uris.placeholder':
'请输入登录 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris':
'登出 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.extra':
'登出 Redirect URI 白名单,应用在请求登录时携带 post_logout_redirect_uri 参数该值需要在白名单中IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.0.message':
'请配置登出 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message':
'登出 Redirect URI 格式不正确',
'pages.app.config.items.login_access.protocol_config.oidc.post_logout_redirect_uris.placeholder':
'请输入登出 Redirect URI',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type': 'SSO 发起方',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.rule.0.message':
'请配置 SSO 发起方',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.0':
'只允许应用发起',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.option.1':
'支持门户和应用发起',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.0':
'门户发起:由 IAM 门户页点击进行 SSO 。',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_type.extra.1':
'应用发起:由应用登录主动发起。',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url': '登录发起地址',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.rule.0.message':
'登录发起地址不能为空',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.field_props':
'请输入登录链接',
'pages.app.config.items.login_access.protocol_config.oidc.init_login_url.extra':
'若您希望由 TopIAM 门户页访问应用,请填写 TopIAM 发起 SSO 请求访问的应用地址。该地址接收到请求,应即刻转向 TopIAM / authorize 授权端点。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced': '显示高级配置',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes': '用户信息范围',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message':
'请勾选用户信息范围',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.extra':
'用户登录后,使用用户信息端点或解析 id_token 可以获取到的已登录用户信息 。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.1.label':
'应用可获取登录用户邮箱信息。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.2.label':
'应用可获取登录用户手机信息。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label':
'应用可获取登录用户详情信息。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live':
'access_token 有效期',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra':
'access_token 用于请求 IAM 接口,过期后需要使用 refresh_token 刷新,或重新登录。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live':
'refresh_token 有效期',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra':
'用于获取新的 access_token 和 id_tokenrefresh_token 过期后,用户需要重新登录。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live':
'id_token 有效期',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live.extra':
'id_token 用于鉴别用户身份JWT格式允许应用使用公钥自行验证用户身份。最小5分钟最大24小时过期后需要使用refresh_token 刷新,或重新登录。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm':
'id_token 签名算法',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra':
'id_token 签名使用的非对称算法。',
'pages.app.config.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message':
'请配置 id_token 签名算法',
'pages.app.config.items.login_access.protocol_config.oidc.config_about': '应用配置信息',
'pages.app.config.items.login_access.protocol_config.oidc.config_about.issuer.extra':
'用于标识 token 发放来源的字段。同时是下述接口的 baseUrl。',
'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint': '发现端点',
'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint.extra':
'用于获取当前 IAM 支持的各端点信息和支持的模式、参数信息,可公开访问。',
'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint': '授权端点',
'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint': '令牌端点',
'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint.extra':
'应用在单点登录过程中,拿到 code 后,从后端发起换取 token 的接口地址。',
'pages.app.config.items.login_access.protocol_config.oidc.revoke_endpoint': '令牌吊销端点',
'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint': '验签公钥端点',
'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint.extra':
'用于验证 id_token、完成 SSO 流程的公钥端点。公钥可能会轮转。',
'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint': '用户信息端点',
'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint.extra':
'在账户登录后,使用 access_token 调用用户信息端点,获取账户基本信息。',
'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint': '结束会话端点',
'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint.extra':
'结束会话端点可用于触发单点注销。',
'pages.app.config.items.login_access.protocol_config.saml2.standard': 'SAML 单点登录',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.extra':
'SP 元数据为应用侧生成的 SSO 配置文件。若已有元数据,直接导入,可自动填充下方 SSO 参数,简单快捷。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.placeholder':
'请输入应用 metadata 地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.meta_data_url.label':
'上传 SP 元数据 ',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.placeholder':
'请输入单点登录地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.extra':
'应用的 SAML SSO 核心地址,与 IAM 交互处理单点登录请求。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_acs_url.rule.0.message':
'请输入单点登录地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.placeholder':
'请输入应用唯一标识',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.extra':
'应用在 IAM 中的标识,通常在应用侧获取,格式通常为应用 URI。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.sp_entity_id.rule.0':
'请输入应用唯一标识。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type':
'Name ID 类型',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type.extra':
'单点登录时,将选中项作为账户标识,传递给业务系统。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.name_id_value_type.rule.0.message':
'请选择 Name ID',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type':
'SSO 发起方',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.rule.0.message':
'请配置 SSO 发起方',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.option.0':
'只允许应用发起',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.option.1':
'支持门户和应用发起',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.extra.0':
'门户发起:由 IAM 门户页点击进行 SSO 。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_type.extra.1':
'应用发起:由应用登录主动发起。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url':
'登录发起地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.placeholder':
'请输入登录链接',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.extra.0':
'若应用支持 IDP 发起 SAMLResponse SSO 请求,可不填写。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.init_login_url.extra.1':
'若应用仅支持 SP 发起 SSO您又希望实现由 IAM 门户页发起登录请填写此项。门户页访问应用时IAM 会跳转到本地址,应即刻自动向 IAM 发起 SAMLRequest 登录请求。地址可填写为任意受保护资源访问地址,在未登录时会自动触发 SAMLRequest 即可。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced':
'显示高级配置',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state':
'默认RelayState',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.placeholder':
'请输入默认跳转地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.rule.0.message':
'RelayState 格式不正确',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.relay_state.extra':
'DP 发起 SSO 登录成功后,应用应自动跳转的地址。在 SAML Response 中会在 RelayState 参数中传递,应用读取后实现跳转。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion':
'断言',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.audience.extra':
'SAML断言的目标受众默认和SP Entity ID相同。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.audience.placeholder':
'请输入SAML断言的目标受众',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format':
'NameID 格式',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format.rule.0.message':
'请选择 NameID 字段格式',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.name_id_format.extra':
'SAML Response 中指定账户标识 NameID 字段格式。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding':
'ACS 绑定类型',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding.rule.0.message':
'请选择 ACS 绑定类型',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.acs_binding.extra':
'指定IDP向SP发送断言请求的方式。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypted':
'是否加密断言',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypted.rule.0.message':
'请选择是否对断言加密。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm':
'断言加密算法。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm.extra':
'断言加密使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_encrypt_algorithm.rule.0.message':
'请选择断言加密使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed':
'是否签名断言',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed.rule.0.message':
'请选择是否对断言签名',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_signed.extra':
'是否对断言使用IdP的证书签名对应SP元数据文件中“WantAssertionsSigned”值。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm':
'断言签名算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm.extra':
'断言签名使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.assert_sign_algorithm.rule.0.message':
'请选择签名使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref':
'身份认证上下文',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref.extra':
'SAML 身份认证上下文',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.authn_context_classref.rule.0.message':
'请选择身份认证上下文',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements':
'断言属性',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name':
'属性名称',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name.rule.0':
'此项为必填项',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name_format':
'名称格式',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.name_format.rule.0':
'此项为必填项',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression':
'属性值',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.tooltip':
'支持的变量:',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.placeholder':
'变量值',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.value_expression.rule.0':
'此项为必填项',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.columns.option':
'操作',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.recordCreatorProps':
'添加断言属性',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.assertion.attribute_statements.editable':
'您确定要删除此断言属性吗?',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed':
'响应签名',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.label':
'是否签名响应',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.rule.0.message':
'请选择是否对响应签名',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm':
'响应签名算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm.extra':
'断言响应使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.response_signed.response_sign_algorithm.rule.0.message':
'请选择响应签名使用的算法',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests':
'验证请求',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed':
'验证请求签名',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed.rule.0.message':
'请选择是否验证请求签名',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_requests.sp_requests_signed.extra':
'用来对SAML Request签名进行验证对应SP元数据文件中“AuthnRequestsSigned”值。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout':
'单点注销',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled':
'启用 SLO',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled.rule.0.message':
'请选择是否启用 SLO',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_slo_enabled.extra':
'SP 是否支持单点注销 (SLO) 。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url':
'单点注销 URL',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.rule.0.message':
'单点注销地址格式不正确',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.rule.1.message':
'请输入单点注销地址',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_url.extra':
'会话注销功能,用户在 TopIAM 注销会话后通知该地址。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding':
'单点注销绑定类型',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding.rule.0.message':
'请选择单点注销绑定类型',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.single_point_logout.sp_sls_binding.extra':
'指定了 IDP 向 SP 发送 SLO 请求的方式。',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert':
'签名证书',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert.rule.0.message':
'请填写验证签名证书',
'pages.app.config.items.login_access.protocol_config.saml2.standard.form.advanced.sp_sign_cert.extra':
'SP公钥证书用来验证SAML request的签名对应SP元数据文件中use="signing"证书内容。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about': '应用配置信息',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_meta_endpoint':
'IdP 元数据',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_meta_endpoint.extra':
'若应用支持 metadata 配置信息上传/拉取,可以节省大量配置步骤。请在应用 SSO 配置中寻找是否有 metadata 上传能力。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_entity_id_endpoint':
'IdP 唯一标识',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_entity_id_endpoint.extra':
'AM 在应用中的标识。需要将值填写在应用单点登录配置中。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sso_endpoint.extra':
'SAML 协议支持 SP 发起单点登录,可能需要填写此地址在应用配置中。由 IAM 提供。可以直接访问该地址,进行应用登录。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_slo_endpoint':
'单点退出地址',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_slo_endpoint.extra':
'SAML 协议支持单点退出,可能需要填写此地址在应用配置中。由 IAM 提供。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert':
'签名公钥证书',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.0':
'下载或复制证书,并导入或粘贴到应用中。',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.1':
'复制证书内容',
'pages.app.config.items.login_access.protocol_config.saml2.config_about.idp_sign_cert.extra.2':
'下载证书 .cer 文件',
'pages.app.config.items.login_access.app_account': '应用账户',
'pages.app.config.items.login_access.app_account.alert.message':
'如果您配置使用【系统账户名】,应用账户属性配置后不会生效;如需变更,请在 协议配置 更改配置。',
'pages.app.config.items.login_access.app_account.columns.username': '系统用户',
'pages.app.config.items.login_access.app_account.columns.account': '应用账户',
'pages.app.config.items.login_access.app_account.columns.create_time': '添加时间',
'pages.app.config.items.login_access.app_account.columns.option': '操作',
'pages.app.config.items.login_access.app_account.columns.option.popconfirm.title':
'您确定要删除此应用账户?',
'pages.app.config.items.login_access.app_account.create_app_account': '添加应用账户',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.rule.0.message':
'请选择系统用户',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.placeholder':
'请输入用户名、手机或邮箱搜索用户',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account':
'应用用户名',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message':
'请输入账户访问应用时所使用户名',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password':
'应用用户密码',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message':
'请输入账户访问应用时所使密码',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity':
'应用身份',
'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message':
'请输入账户访问应用时所使身份',
'pages.app.config.items.login_access.access_policy': '访问授权',
'pages.app.config.items.login_access.access_policy.columns.subject_name': '授权主体',
'pages.app.config.items.login_access.access_policy.columns.subject_type': '主体类型',
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user': '用户',
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user_group':
'用户组',
'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.organization':
'组织机构',
'pages.app.config.items.login_access.access_policy.columns.create_time': '授权时间',
'pages.app.config.items.login_access.access_policy.columns.option': '操作',
'pages.app.config.items.login_access.access_policy.columns.option.popconfirm.title':
'您确定要取消主体授权?取消授权后不可自动恢复。',
'pages.app.config.items.login_access.access_policy.create_policy': '添加授权',
'pages.app.config.items.login_access.access_policy.cancel_policy': '取消授权',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type':
'授权类型',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.rule.0.message':
'请选择授权类型',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user':
'授权用户',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.rule.0.message':
'请选择授权用户',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.placeholder':
'请输入用户名搜索用户',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group':
'授权分组',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message':
'请选择授权分组',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization':
'授权组织',
'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message':
'请选择组织节点',
'pages.app.config.items.app_permission': '权限管理',
'pages.app.config.items.app_permission.permission_resource': '资源管理',
'pages.app.config.items.app_permission.permission_role': '角色管理',
'pages.app.config.items.app_permission.permission_audit': '权限审计',
'pages.app.config.items.account_sync': '账户同步',
'pages.app.config.error': '未指定应用',
};

View File

@ -1,101 +0,0 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import type { GenerateStyle, ProAliasToken } from '@ant-design/pro-components';
import { useStyle as useAntdStyle } from '@ant-design/pro-components';
import { ConfigProvider } from 'antd';
import { useContext } from 'react';
const { ConfigContext } = ConfigProvider;
interface AppConfigToken extends ProAliasToken {
antCls: string;
prefixCls: string;
}
const genActionsStyle: GenerateStyle<AppConfigToken> = (token) => {
const { prefixCls, antCls } = token;
return {
[`${prefixCls}`]: {
[`&-main`]: {
display: 'flex',
},
[`&-left`]: {
minHeight: '100%',
overflow: 'auto',
marginRight: token.margin,
width: '200px',
[`&-menu`]: {
height: 'calc(100vh - 178px)',
[`&${antCls}-menu-light${antCls}-menu-root${antCls}-menu-inline`]: {
'border-inline-end': 'none',
},
[`&${antCls}-menu-light:not(${antCls}-menu-horizontal) ${antCls}-menu-item-selected`]: {
'background-color': token.layout?.sider?.colorBgMenuItemSelected,
color: token.layout?.sider?.colorTextMenuSelected,
},
[`&${antCls}-menu-light:not(${antCls}-menu-horizontal) ${antCls}-menu-item:not(${antCls}-menu-item-selected):active`]:
{
'background-color': token.layout?.sider?.colorBgMenuItemSelected,
color: token.layout?.sider?.colorTextMenuActive,
},
[`&${antCls}-menu-light ${antCls}-menu-submenu-selected >${antCls}-menu-submenu-title`]: {
color: token.layout?.sider?.colorTextMenuSelected,
},
},
},
[`&-right`]: {
flex: 1,
minHeight: '100%',
overflow: 'auto',
},
},
[`@media screen and (max-width: ${token.screenXL}px)`]: {
[`${prefixCls}`]: {
['&-main']: {
flexDirection: 'column',
},
['&-left']: {
width: '100%',
border: 'none',
marginRight: 0,
marginBottom: token.margin,
[`&-menu`]: {
height: 'auto',
},
},
},
},
};
};
export default function useStyle(prefixCls?: string) {
const { getPrefixCls } = useContext(ConfigContext || ConfigProvider.ConfigContext);
const antCls = `.${getPrefixCls()}`;
return useAntdStyle('AppConfig', (token) => {
const appConfigToken: AppConfigToken = {
...token,
prefixCls: `.${prefixCls}`,
antCls,
};
return [genActionsStyle(appConfigToken)];
});
}

View File

@ -0,0 +1,109 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { ModalForm, ProFormText, ProFormTextArea } from '@ant-design/pro-components';
import { Form, Spin } from 'antd';
import React, { useState } from 'react';
import { useIntl } from '@@/exports';
import { random } from '@/utils/utils';
export default (props: {
open: boolean;
onFinish: (formData: Record<string, string>) => Promise<boolean | void>;
onCancel: (e: React.MouseEvent<HTMLButtonElement>) => void;
}) => {
const { open, onCancel, onFinish } = props;
const [form] = Form.useForm();
const intl = useIntl();
const [loading, setLoading] = useState<boolean>(false);
return (
<ModalForm
title={intl.formatMessage({ id: 'pages.app_group.create.modal_form.title' })}
form={form}
open={open}
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
width={'500px'}
layout={'horizontal'}
labelAlign={'right'}
preserve={false}
autoFocusFirstInput
onOpenChange={(visible) => {
if (visible) {
form.setFieldsValue({ code: random(9) });
}
}}
modalProps={{
maskClosable: true,
destroyOnClose: true,
onCancel: onCancel,
}}
onFinish={async (values: Record<string, string>) => {
setLoading(true);
await onFinish(values).finally(() => {
setLoading(false);
});
}}
>
<Spin spinning={loading}>
<ProFormText
label={intl.formatMessage({ id: 'pages.app_group.modal_form.name' })}
name="name"
placeholder={intl.formatMessage({ id: 'pages.app_group.modal_form.name.placeholder' })}
fieldProps={{
maxLength: 8,
}}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app_group.modal_form.name.rule.0.message',
}),
},
]}
/>
<ProFormText
label={intl.formatMessage({ id: 'pages.app_group.modal_form.code' })}
placeholder={intl.formatMessage({ id: 'pages.app_group.modal_form.code.placeholder' })}
name="code"
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app_group.modal_form.code.rule.0.message',
}),
},
]}
/>
<ProFormTextArea
label={intl.formatMessage({ id: 'pages.app_group.modal_form.remark' })}
name="remark"
fieldProps={{
placeholder: intl.formatMessage({
id: 'pages.app_group.modal_form.remark.placeholder',
}),
rows: 2,
maxLength: 20,
autoComplete: 'off',
showCount: true,
}}
/>
</Spin>
</ModalForm>
);
};

View File

@ -0,0 +1,19 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import CreateModal from './CreateModal';
export default CreateModal;

View File

@ -0,0 +1,19 @@
/*
* eiam-console - Employee Identity and Access Management
* Copyright © 2022-Present Jinan Yuanchuang Network Technology Co., Ltd. (support@topiam.cn)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import UpdateModal from './UpdateModal';
export default UpdateModal;

View File

@ -15,7 +15,7 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
import { getAllAppGroupList, getAppList } from '@/services/app';
import { getAllAppGroupList, getAppList, removeApp } from '@/services/app';
import { PlusOutlined, QuestionCircleOutlined, GroupOutlined } from '@ant-design/icons';
import type { ActionType } from '@ant-design/pro-components';
import { PageContainer, ProList } from '@ant-design/pro-components';
@ -25,7 +25,7 @@ import { history, useIntl } from '@umijs/max';
import useStyle from './style';
import classnames from 'classnames';
import { AppList } from './data.d';
import { disableApp, enableApp, removeApp } from './service';
import { disableApp, enableApp } from './service';
const prefixCls = 'app-list';
@ -111,7 +111,7 @@ export default () => {
<span
onClick={() => {
history.push(
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
`/app/detail?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
);
}}
>
@ -183,14 +183,12 @@ export default () => {
</Popconfirm>
),
<a
key="config"
key="details"
onClick={() => {
history.push(
`/app/config?id=${row.id}&protocol=${row.protocol}&name=${row.name}`,
);
history.push(`/app/detail?id=${row.id}`);
}}
>
{intl.formatMessage({ id: 'app.manage' })}
{intl.formatMessage({ id: 'pages.app.list.actions.detail' })}
</a>,
<Popconfirm
title={intl.formatMessage({

View File

@ -31,11 +31,4 @@ export async function disableApp(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/disable/${id}`, { method: 'PUT' });
}
/**
* Remove Application
*/
export async function removeApp(id: string): Promise<API.ApiResult<boolean>> {
return request<API.ApiResult<boolean>>(`/api/v1/app/delete/${id}`, {
method: 'DELETE',
});
}