优化应用

pull/50/MERGE
smallbun 2023-09-16 23:39:03 +08:00
parent 7f983484aa
commit b1a0693eac
27 changed files with 2334 additions and 550 deletions

View File

@ -1,143 +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 type GetApp = {
id: string;
type: string;
name: string;
icon: string;
template: string;
protocol: string;
protocolName: string;
clientId: string;
clientSecret: string;
//sso发起方
initLoginType: string;
//sso登录链接
initLoginUrl: string;
nameIdValueType: string;
//授权范围
authorizationType: string;
enabled: boolean;
remark: string;
groupIds: string[];
};
/**
*
*/
export type AppAccessPolicyList = {
id: string;
//主体ID
subjectId: string;
//主体名称
subjectName: string;
//主体类型
subjectType: string;
//应用类型
appType: string;
appProtocol: string;
//Effect
effect: string;
};
/**
*
*/
type AppPermissionRoleList = {
id: string;
name: string;
code: string;
enabled: boolean;
appId: string;
remark: string;
};
/**
*
*/
type GetAppPermissionRole = {
id: string;
name: string;
code: string;
enabled: boolean;
appId: string;
remark: string;
};
/**
*
*/
type AppPermissionResourceList = {
id: string;
name: string;
code: string;
enabled: boolean;
desc: string;
appId: string;
};
/**
*
*/
type AppPermissionResourceActionList = {
id: string;
name: string;
code: string;
desc: string;
appId: string;
menus: {
access: string;
id: string;
name: string;
}[];
datas: {
access: string;
id: string;
name: string;
}[];
buttons: {
access: string;
id: string;
name: string;
}[];
apis: {
access: string;
id: string;
name: string;
}[];
others: {
access: string;
id: string;
name: string;
}[];
};
/**
*
*/
type AppPermissionPolicyList = {
id: string;
name: string;
code: string;
enabled: boolean;
desc: string;
appId: string;
};

View File

@ -1,279 +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 { request } from '@@/exports';
import {
AppPermissionPolicyList,
AppPermissionResourceActionList,
AppPermissionResourceList,
AppPermissionRoleList,
GetApp,
GetAppPermissionRole,
} from './data.d';
import { RequestData } from '@ant-design/pro-components';
/**
* Get Application
*/
export async function getApp(id: string): Promise<API.ApiResult<GetApp>> {
return request<API.ApiResult<GetApp>>(`/api/v1/app/get/${id}`, {
method: 'GET',
});
}
/**
* Get Role list
*/
export async function getPermissionRoleList(
params: Record<string, any>,
): Promise<RequestData<AppPermissionRoleList>> {
return request<API.ApiResult<AppPermissionRoleList>>('/api/v1/app/permission/role/list', {
params,
}).then((result) => {
const data: RequestData<AppPermissionRoleList> = {
data: result?.result?.list,
success: result?.success,
total: result?.result?.pagination ? result?.result?.pagination.total : 0,
};
return Promise.resolve(data);
});
}
/**
* Create Role
*/
export async function createPermissionRole(
params: Record<string, any>,
): Promise<API.ApiResult<boolean>> {
return request('/api/v1/app/permission/role/create', {
data: params,
method: 'POST',
requestType: 'form',
});
}
/**
* Update Role
*/
export async function updatePermissionRole(
params: Record<string, string>,
): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/update`, {
data: params,
method: 'PUT',
requestType: 'form',
});
}
/**
* Enable Role
*/
export async function enablePermissionRole(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/enable/${id}`, {
method: 'PUT',
});
}
/**
* Disable Role
*/
export async function disableRole(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/disable/${id}`, {
method: 'PUT',
});
}
/**
* Remove role
*/
export async function deletePermissionRole(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/delete/${id}`, {
method: 'DELETE',
});
}
/**
* Remove role
*/
export async function batchDeletePermissionRole(
ids: (number | string)[],
): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/delete`, {
params: { ids },
method: 'DELETE',
});
}
/**
* Get Role details
*/
export async function getPermissionRole(id: string): Promise<API.ApiResult<GetAppPermissionRole>> {
return request(`/api/v1/app/permission/role/${id}`);
}
/**
*
*
* @param appId
* @param type
* @param value
* @param id
*/
export async function permissionRoleParamCheck(
appId: string,
type: string,
value: string,
id?: string,
): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/role/param_check`, {
params: { appId, id, type, value },
method: 'GET',
});
}
/**
* Get Resource list
*/
export async function getPermissionResourceList(
params: Record<string, any>,
): Promise<RequestData<AppPermissionResourceList>> {
return request<API.ApiResult<AppPermissionResourceList>>('/api/v1/app/permission/resource/list', {
params,
}).then((result) => {
const data: RequestData<AppPermissionResourceList> = {
data: result?.result?.list,
success: result?.success,
total: result?.result?.pagination ? result?.result?.pagination.total : 0,
};
return Promise.resolve(data);
});
}
/**
* Get Permission Policy list
*/
export async function getPermissionPolicyList(
params: Record<string, any>,
): Promise<RequestData<AppPermissionPolicyList>> {
return request<API.ApiResult<AppPermissionPolicyList>>('/api/v1/app/permission/policy/list', {
params,
}).then((result) => {
const data: RequestData<AppPermissionPolicyList> = {
data: result?.result?.list,
success: result?.success,
total: result?.result?.pagination ? result?.result?.pagination.total : 0,
};
return Promise.resolve(data);
});
}
/**
* Get Resource
*/
export async function getPermissionResource(
id: string,
): Promise<API.ApiResult<Record<string, any>>> {
return request(`/api/v1/app/permission/resource/get/${id}`, {
method: 'GET',
requestType: 'json',
});
}
/**
* Get Permission action list
*/
export async function getPermissionActionList(
params: Record<string, any>,
): Promise<API.ApiResult<AppPermissionResourceActionList>> {
return request(`/api/v1/app/permission/action/list`, {
method: 'GET',
params,
});
}
/**
* Create Resource
*/
export async function createPermissionResource(
params: Record<string, any>,
): Promise<API.ApiResult<boolean>> {
return request('/api/v1/app/permission/resource/create', {
data: params,
method: 'POST',
requestType: 'json',
});
}
/**
* Delete Resource
*/
export async function deletePermissionResource(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/resource/delete/${id}`, {
method: 'DELETE',
});
}
/**
* Update Resource
*/
export async function updatePermissionResource(
params: Record<string, string>,
): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/resource/update`, {
data: params,
method: 'PUT',
requestType: 'json',
});
}
/**
*
*
* @param appId
* @param type
* @param value
* @param id
*/
export async function permissionResourceParamCheck(
appId: string,
type: string,
value: string,
id?: string,
): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/resource/param_check`, {
params: { appId, id, type, value },
method: 'GET',
});
}
/**
* Enable Resource
*/
export async function enableResource(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/resource/enable/${id}`, {
method: 'PUT',
});
}
/**
* Disable Resource
*/
export async function disableResource(id: string): Promise<API.ApiResult<boolean>> {
return request(`/api/v1/app/permission/resource/disable/${id}`, {
method: 'PUT',
});
}

View File

@ -0,0 +1,235 @@
/*
* 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 { PageContainer, ProDescriptions, RouteContext } from '@ant-design/pro-components';
import { useAsyncEffect } from 'ahooks';
import { App, Button, Skeleton } from 'antd';
import React, { useState } from 'react';
import AccessPolicy from './components/AccessPolicy';
import AppAccount from './components/AppAccount';
import AppConfig from './components/AppConfig';
import { ConfigTabs } from './constant';
import AppProtocol from './components/AppProtocol';
import queryString from 'query-string';
import { useIntl, useLocation } from '@umijs/max';
import { GetApp } from './data.d';
import { getApp } from './service';
import { ExclamationCircleFilled } from '@ant-design/icons';
import { removeApp } from '@/services/app';
export default () => {
const [tabActiveKey, setTabActiveKey] = useState<string>(ConfigTabs.app_config);
const [loading, setLoading] = useState<boolean>(true);
const location = useLocation();
const intl = useIntl();
const { message, modal } = App.useApp();
const [app, setApp] = useState<GetApp>();
const query = queryString.parse(location.search) as {
id: string;
name: string;
type: string;
protocol: string;
};
const { type, id } = query as {
id: string;
type: ConfigTabs;
};
useAsyncEffect(async () => {
if (!id) {
message.error(intl.formatMessage({ id: 'pages.app.config.detail.error' }));
history.push('/app');
return;
}
if (!type || !ConfigTabs[type]) {
setTabActiveKey(ConfigTabs.app_config);
history.replace({
pathname: location.pathname,
search: queryString.stringify({
type: ConfigTabs.app_config,
id,
}),
});
return;
}
}, []);
useAsyncEffect(async () => {
setLoading(true);
const { result, success } = await getApp(id).finally(() => {
setLoading(false);
});
if (success && result) {
setApp(result);
}
}, [id]);
const description = (
<RouteContext.Consumer>
{({ isMobile }) =>
loading ? (
<Skeleton active paragraph={{ rows: 1 }} />
) : (
<ProDescriptions
size="small"
column={isMobile ? 1 : 2}
dataSource={{ ...app }}
style={{ marginBlockEnd: -16 }}
>
<ProDescriptions.Item
dataIndex="type"
label={intl.formatMessage({ id: 'pages.app.config.detail.config.type' })}
editable={false}
valueEnum={{
custom_made: {
text: intl.formatMessage({
id: 'pages.app.config.detail.config.type.value_enum.custom_made',
}),
},
standard: {
text: intl.formatMessage({
id: 'pages.app.config.detail.config.type.value_enum.standard',
}),
},
self_developed: {
text: intl.formatMessage({
id: 'pages.app.config.detail.config.type.value_enum.self_developed',
}),
},
}}
/>
<ProDescriptions.Item
dataIndex="enabled"
label={intl.formatMessage({ id: 'pages.app.config.detail.config.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="clientId"
ellipsis
label={intl.formatMessage({ id: 'pages.app.config.detail.config.client_id' })}
valueType={'text'}
editable={false}
copyable={true}
/>
<ProDescriptions.Item
dataIndex="clientSecret"
label={intl.formatMessage({ id: 'pages.app.config.detail.config.client_secret' })}
valueType={'password'}
editable={false}
copyable={true}
/>
<ProDescriptions.Item
dataIndex="createTime"
label={intl.formatMessage({ id: 'pages.app.config.detail.config.create_time' })}
valueType={'dateTime'}
copyable={false}
editable={false}
/>
</ProDescriptions>
)
}
</RouteContext.Consumer>
);
return (
<PageContainer
title={loading ? <Skeleton.Input style={{ width: 50 }} active size={'small'} /> : app?.name}
style={{ overflow: 'hidden' }}
onBack={() => {
history.push('/app');
}}
loading={loading}
extra={[
<Button
key="delete"
type="primary"
danger
onClick={() => {
const confirmed = modal.error({
centered: true,
title: intl.formatMessage({
id: 'pages.app.config.detail.extra.delete.confirm_title',
}),
icon: <ExclamationCircleFilled />,
content: intl.formatMessage({
id: 'pages.app.config.detail.extra.delete.confirm_content',
}),
okText: intl.formatMessage({ id: 'app.confirm' }),
okType: 'danger',
okCancel: true,
cancelText: intl.formatMessage({ id: 'app.cancel' }),
onOk: async () => {
const { success } = await removeApp(id);
if (success) {
message.success(intl.formatMessage({ id: 'app.operation_success' }));
confirmed.destroy();
}
},
});
}}
>
{intl.formatMessage({ id: 'pages.app.config.detail.extra.delete' })}
</Button>,
]}
tabActiveKey={tabActiveKey}
onTabChange={setTabActiveKey}
tabList={[
{
key: ConfigTabs.app_config,
tab: intl.formatMessage({ id: 'pages.app.config.detail.config' }),
},
{
key: ConfigTabs.login_access,
tab: intl.formatMessage({ id: 'pages.app.config.detail.items.login_access' }),
},
{
key: ConfigTabs.app_account,
tab: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.app_account',
}),
},
{
key: ConfigTabs.access_policy,
tab: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.access_policy',
}),
},
]}
content={description}
>
{app && (
<>
{/*基本信息*/}
{ConfigTabs.app_config === tabActiveKey && <AppConfig app={app} />}
{/*协议配置*/}
{ConfigTabs.login_access === tabActiveKey && <AppProtocol appId={app?.id} />}
{/*应用账户*/}
{ConfigTabs.app_account === tabActiveKey && (
<AppAccount appId={app?.id} protocol={app.protocol} />
)}
{/*访问策略*/}
{ConfigTabs.access_policy === tabActiveKey && <AccessPolicy appId={app?.id} />}
</>
)}
</PageContainer>
);
};

View File

@ -57,7 +57,7 @@ const CreateAppAccessPolicy = (props: {
<>
<ModalForm
title={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy',
})}
width={600}
open={open}
@ -81,7 +81,7 @@ const CreateAppAccessPolicy = (props: {
<ProFormRadio.Group
name="subjectType"
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type',
})}
initialValue={AccessPolicyType.USER}
fieldProps={{
@ -94,19 +94,19 @@ const CreateAppAccessPolicy = (props: {
{
value: AccessPolicyType.USER,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user',
}),
},
{
value: AccessPolicyType.USER_GROUP,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user_group',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user_group',
}),
},
{
value: AccessPolicyType.ORGANIZATION,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.organization',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.organization',
}),
},
]}
@ -114,7 +114,7 @@ const CreateAppAccessPolicy = (props: {
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.rule.0.message',
}),
},
]}
@ -127,21 +127,21 @@ const CreateAppAccessPolicy = (props: {
<>
<Form.Item
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user',
})}
name={'subjectIds'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.rule.0.message',
}),
},
]}
>
<UserSelect
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.placeholder',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.placeholder',
})}
mode={'multiple'}
/>
@ -153,21 +153,21 @@ const CreateAppAccessPolicy = (props: {
return (
<Form.Item
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group',
})}
name={'subjectIds'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message',
}),
},
]}
>
<UserGroupSelect
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message',
})}
mode={'multiple'}
/>
@ -179,21 +179,21 @@ const CreateAppAccessPolicy = (props: {
<>
<Form.Item
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization',
})}
name={'subjectIds'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message',
}),
},
]}
>
<OrgCascader
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message',
})}
/>
</Form.Item>
@ -216,7 +216,7 @@ export default (props: { appId: string }) => {
const columns: ProColumns<AppAPI.AppAccessPolicyList>[] = [
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_name',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_name',
}),
dataIndex: 'subjectName',
ellipsis: true,
@ -224,7 +224,7 @@ export default (props: { appId: string }) => {
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type',
}),
dataIndex: 'subjectType',
valueType: 'select',
@ -232,24 +232,24 @@ export default (props: { appId: string }) => {
valueEnum: {
USER: {
text: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user',
}),
},
USER_GROUP: {
text: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.user_group',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user_group',
}),
},
ORGANIZATION: {
text: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.subject_type.value_enum.organization',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.organization',
}),
},
},
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.create_time',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.create_time',
}),
align: 'center',
ellipsis: true,
@ -259,7 +259,7 @@ export default (props: { appId: string }) => {
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.option',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.option',
}),
valueType: 'option',
key: 'option',
@ -269,7 +269,7 @@ export default (props: { appId: string }) => {
render: (text, record) => [
<Popconfirm
title={intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.columns.option.popconfirm.title',
id: 'pages.app.config.detail.items.login_access.access_policy.columns.option.popconfirm.title',
})}
placement="bottomRight"
icon={
@ -299,7 +299,7 @@ export default (props: { appId: string }) => {
}}
>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.cancel_policy',
id: 'pages.app.config.detail.items.login_access.access_policy.cancel_policy',
})}
</a>
</Popconfirm>,
@ -326,11 +326,6 @@ export default (props: { appId: string }) => {
pagination={{
defaultPageSize: 5,
}}
style={{
height: 'calc(100vh - 178px)',
overflow: 'auto',
}}
cardProps={{ style: { minHeight: '100%' } }}
dateFormatter="string"
toolBarRender={() => [
<Button
@ -342,7 +337,7 @@ export default (props: { appId: string }) => {
}}
>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.access_policy.create_policy',
id: 'pages.app.config.detail.items.login_access.access_policy.create_policy',
})}
</Button>,
]}

View File

@ -21,13 +21,13 @@ import { PlusOutlined, QuestionCircleOutlined } from '@ant-design/icons';
import type { ActionType, ProColumns } from '@ant-design/pro-components';
import { ModalForm, ProFormText, ProTable } from '@ant-design/pro-components';
import { Alert, App, Button, Form, Popconfirm, Table } from 'antd';
import { App, Button, Form, Popconfirm, Table } from 'antd';
import { useRef } from 'react';
import { AppProtocolType } from '@/constant';
import { Base64 } from 'js-base64';
import { useIntl } from '@umijs/max';
export default (props: { appId: string; protocol: AppProtocolType }) => {
export default (props: { appId: string; protocol: AppProtocolType | string }) => {
const actionRef = useRef<ActionType>();
const intl = useIntl();
const { message } = App.useApp();
@ -35,7 +35,7 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
const columns: ProColumns<AppAPI.AppAccountList>[] = [
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.username',
id: 'pages.app.config.detail.items.login_access.app_account.columns.username',
}),
dataIndex: 'username',
ellipsis: true,
@ -43,14 +43,14 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.account',
id: 'pages.app.config.detail.items.login_access.app_account.columns.account',
}),
dataIndex: 'account',
ellipsis: true,
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.create_time',
id: 'pages.app.config.detail.items.login_access.app_account.columns.create_time',
}),
dataIndex: 'createTime',
valueType: 'dateTime',
@ -59,7 +59,7 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
},
{
title: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.option',
id: 'pages.app.config.detail.items.login_access.app_account.columns.option',
}),
valueType: 'option',
key: 'option',
@ -69,7 +69,7 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
render: (text, record) => [
<Popconfirm
title={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.option.popconfirm.title',
id: 'pages.app.config.detail.items.login_access.app_account.columns.option.popconfirm.title',
})}
placement="bottomRight"
icon={
@ -117,7 +117,7 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
<>
<ModalForm
title={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account',
})}
width={500}
form={form}
@ -125,7 +125,7 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
trigger={
<Button key="button" icon={<PlusOutlined />} type="primary">
{intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account',
})}
</Button>
}
@ -155,21 +155,21 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
>
<Form.Item
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.columns.username',
id: 'pages.app.config.detail.items.login_access.app_account.columns.username',
})}
name={'userId'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.user_id.rule.0.message',
}),
},
]}
>
<UserSelect
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.user_id.placeholder',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.user_id.placeholder',
})}
/>
</Form.Item>
@ -178,34 +178,34 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
<>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.account',
})}
name={'account'}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message',
}),
},
]}
/>
<ProFormText.Password
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.password',
})}
name={'password'}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message',
}),
},
]}
@ -215,17 +215,17 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
//非Form协议
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.app_identity',
})}
name={'account'}
placeholder={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message',
id: 'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message',
}),
},
]}
@ -237,14 +237,6 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
};
return (
<>
<Alert
banner
type={'info'}
message={intl.formatMessage({
id: 'pages.app.config.items.login_access.app_account.alert.message',
})}
style={{ marginBottom: 16 }}
/>
<ProTable<AppAPI.AppAccountList>
columns={columns}
actionRef={actionRef}
@ -258,11 +250,6 @@ export default (props: { appId: string; protocol: AppProtocolType }) => {
params={{ appId: appId }}
rowKey="id"
search={{}}
style={{
height: 'calc(100vh - 244px)',
overflow: 'auto',
}}
cardProps={{ style: { minHeight: '100%' } }}
options={false}
pagination={{
defaultPageSize: 5,

View File

@ -0,0 +1,202 @@
/*
* 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,
ProForm,
ProFormItem,
ProFormSelect,
ProFormText,
ProFormTextArea,
} from '@ant-design/pro-components';
import { useAsyncEffect } from 'ahooks';
import { App, Col, FormInstance, Row, Space, Upload, UploadFile, UploadProps } from 'antd';
import React, { useRef, useState } from 'react';
import { history, useIntl, useLocation } from '@umijs/max';
import queryString from 'query-string';
import { GetApp } from '../../data.d';
import { uploadFile } from '@/services/upload';
import { LoadingOutlined, PlusOutlined } from '@ant-design/icons';
import { RcFile, UploadChangeParam } from 'antd/es/upload';
import { Container } from '@/components/Container';
const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 12 },
};
const AppBasic = (props: { app: GetApp }) => {
const { app } = props;
const intl = useIntl();
const form = useRef<FormInstance>();
const location = useLocation();
const useApp = App.useApp();
const [iconUploadLoading, setIconUploadLoading] = useState<boolean>(false);
const [icon, setIcon] = useState<string>();
const query = queryString.parse(location.search) as {
id: string;
name: string;
type: string;
protocol: string;
};
useAsyncEffect(async () => {
form?.current?.setFieldsValue({
...app,
});
setIcon(app?.icon);
}, []);
const getBase64 = (img: RcFile, callback: (url: string) => void) => {
const reader = new FileReader();
reader.addEventListener('load', () => callback(reader.result as string));
reader.readAsDataURL(img);
};
const handleChange: UploadProps['onChange'] = (info: UploadChangeParam<UploadFile>) => {
if (info.file.status === 'uploading') {
setIconUploadLoading(true);
return;
}
if (info.file.status === 'error') {
setIconUploadLoading(false);
}
if (info.file.status === 'done') {
getBase64(info.file.originFileObj as RcFile, (url) => {
setIconUploadLoading(false);
setIcon(url);
});
}
};
const uploadButton = (
<div>
{iconUploadLoading ? <LoadingOutlined /> : <PlusOutlined />}
<div style={{ marginTop: 8 }}>{intl.formatMessage({ id: 'custom.upload' })}</div>
</div>
);
return (
<ProCard>
<Container maxWidth={1152}>
<ProForm
layout="horizontal"
formRef={form}
{...formItemLayout}
submitter={{
render: (props, doms) => {
return (
<Row>
<Col span={12} offset={6}>
<Space>{doms}</Space>
</Col>
</Row>
);
},
}}
onFinish={async (params) => {
const { success } = await updateApp(params);
if (success) {
useApp.message.success(intl.formatMessage({ id: 'app.operation_success' }));
history.replace(
`/app/detail?id=${query.id}&name=${params.name}&protocol=${query.protocol}&type=${query.type}`,
);
return Promise.resolve(true);
}
return Promise.resolve(true);
}}
>
<ProFormText name={'id'} hidden />
<ProFormText
label={intl.formatMessage({ id: 'pages.app.config.detail.config.name' })}
name={'name'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.config.name.rule.0.message',
}),
},
]}
/>
<ProFormItem
label={intl.formatMessage({ id: 'pages.app.config.detail.config.icon' })}
extra={
<div style={{ color: 'rgba(0, 0, 0, 0.45)' }}>
<span>{intl.formatMessage({ id: 'pages.app.config.detail.config.icon.desc.1' })}</span>
<br />
<span>{intl.formatMessage({ id: 'pages.app.config.detail.config.icon.desc.2' })}</span>
</div>
}
>
<Upload
listType="picture-card"
multiple={false}
accept="image/png, image/jpeg"
onChange={handleChange}
showUploadList={false}
customRequest={async ({ file, onProgress, onSuccess, onError }) => {
if (!file) {
return;
}
const result = await uploadFile(file, undefined, (ev) => {
const percent = (ev.loaded / ev.total) * 100;
// 计算出上传进度,调用组件进度条方法
onProgress?.({ percent });
});
if (result.success && result.result) {
onSuccess?.(result.result);
return;
}
onError?.(new Error(result.message), result);
useApp.message.error(result.message);
}}
>
{icon ? <img src={icon} alt="avatar" style={{ width: '100%' }} /> : uploadButton}
</Upload>
</ProFormItem>
<ProFormSelect
name="groupIds"
mode="multiple"
fieldProps={{
maxTagCount: 'responsive',
}}
label={intl.formatMessage({ id: 'pages.app.config.detail.config.group' })}
request={async () => {
const { success, data } = await getAllAppGroupList({}, {}, {});
if (success && data) {
return data.map((i) => {
return { label: i.name, value: i.id };
});
}
return [];
}}
/>
<ProFormTextArea
label={intl.formatMessage({ id: 'pages.app.config.detail.config.remark' })}
name={'remark'}
fieldProps={{ rows: 2, maxLength: 200, showCount: false }}
/>
</ProForm>
</Container>
</ProCard>
);
};
export default AppBasic;

View File

@ -26,11 +26,9 @@ import FromConfig from './FromProtocolConfig';
import JwtConfig from './JwtProtocolConfig';
import OidcConfig from './OidcProtocolConfig';
import { GetApp } from '../../data.d';
import { useIntl } from '@@/exports';
export default (props: { appId: string }) => {
const { appId } = props;
const intl = useIntl();
const [loading, setLoading] = useState<boolean>(true);
const [app, setApp] = useState<GetApp>();
useAsyncEffect(async () => {
@ -52,12 +50,7 @@ export default (props: { appId: string }) => {
return <Component app={app} />;
};
return (
<ProCard
title={intl.formatMessage({ id: 'pages.app.config.items.login_access.protocol_config' })}
style={{ height: 'calc(100vh - 178px)', overflow: 'auto' }}
bodyStyle={{ height: '100%' }}
headerBordered
>
<ProCard>
<Skeleton loading={loading} active={true} paragraph={{ rows: 5 }}>
{app && ComponentByKey({ key: app?.protocol, app: app })}
</Skeleton>

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 { SsoScope } from '@/pages/app/AppConfig/constant';
import { SsoScope } from '@/pages/app/AppDetail/constant';
import { ProFormSelect } from '@ant-design/pro-components';
import { useIntl } from '@@/exports';
@ -31,18 +31,18 @@ export default () => {
return (
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.common.authorization_type',
id: 'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type',
})}
name={'authorizationType'}
allowClear={false}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.common.authorization_type.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.common.authorization_type.rule.0.message',
id: 'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.rule.0.message',
}),
},
]}
@ -50,13 +50,13 @@ export default () => {
{
value: SsoScope.AUTHORIZATION,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.0',
id: 'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.option.0',
}),
},
{
value: SsoScope.ALL_ACCESS,
label: intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.common.authorization_type.option.1',
id: 'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.option.1',
}),
},
]}

View File

@ -20,6 +20,17 @@ import { useAsyncEffect } from 'ahooks';
import { Collapse, Form, Typography } from 'antd';
import { useIntl } from '@umijs/max';
import Alert from '@/components/Alert';
import { createStyles } from 'antd-style';
import { ColProps } from 'antd/es/grid/col';
const useStyles = createStyles(({ prefixCls }) => ({
alert: {
[`.${prefixCls}-alert-content .${prefixCls}-alert-description .${prefixCls}-form-item:last-child`]:
{
marginBottom: '0 !important',
},
},
}));
/**
*
@ -30,10 +41,23 @@ export default (props: {
appId: string;
protocolEndpoint: Record<string, string>;
collapsed?: boolean;
labelCol?: ColProps;
wrapperCol?: ColProps;
}) => {
const [configForm] = Form.useForm();
const { protocolEndpoint, appId, collapsed = true } = props;
const {
protocolEndpoint,
appId,
collapsed = true,
labelCol = {
span: 6,
},
wrapperCol = {
span: 12,
},
} = props;
const intl = useIntl();
const { styles } = useStyles();
useAsyncEffect(async () => {
configForm.setFieldsValue(protocolEndpoint);
@ -42,8 +66,8 @@ export default (props: {
return (
<ProForm
layout={'horizontal'}
labelCol={{ xs: { span: 24 }, sm: { span: 6 } }}
wrapperCol={{ xs: { span: 24 }, sm: { span: 12 } }}
labelCol={labelCol}
wrapperCol={wrapperCol}
labelAlign={'right'}
submitter={false}
labelWrap
@ -59,22 +83,23 @@ export default (props: {
label: (
<a>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.config_about',
id: 'pages.app.config.detail.items.login_access.protocol_config.form.config_about',
})}
</a>
),
children: (
<Alert
type={'grey'}
className={styles.alert}
description={
<>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.form.config_about.idp_sso_endpoint',
})}
name={'idpSsoEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.form.config_about.idp_sso_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.form.config_about.idp_sso_endpoint.extra',
})}
readonly
proFieldProps={{

View File

@ -0,0 +1,406 @@
/*
* 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';
import { Container } from '@/components/Container';
const layout = {
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
banner
type={'info'}
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.detail.items.login_access.protocol_config.form',
})}
</a>{' '}
</span>
}
/>
<br />
<Container maxWidth={1152}>
<ProForm
form={form}
requiredMark={true}
layout={'horizontal'}
{...layout}
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.detail.items.login_access.protocol_config.form.login_url',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.login_url.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.login_url.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.login_url.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.login_url.rule.1.message',
}),
},
]}
/>
<ProFormText
name={'usernameField'}
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_field',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_field.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_field.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_field.rule.0.message',
}),
},
]}
/>
<ProFormText
name={'passwordField'}
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_field',
})}
placeholder={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_field.placeholder',
})}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_field.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_field.rule.0.message',
}),
},
]}
/>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.items.login_access.protocol_config.form.password_encrypt_key',
})}
name={'passwordEncryptKey'}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_key.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message',
}),
},
]}
/>
) : (
<></>
);
}}
</ProFormDependency>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.items.login_access.protocol_config.form.username_encrypt_key',
})}
name={'usernameEncryptKey'}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_key.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message',
}),
},
]}
/>
) : (
<></>
);
}}
</ProFormDependency>
<ProFormRadio.Group
name={'submitType'}
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.form.submit_type.rule.0.message',
}),
},
]}
/>
<Divider />
{/*授权类型*/}
<AuthorizationType />
<Divider />
<ProForm.Item
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0',
}),
},
],
},
},
{
title: intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0',
}),
},
],
},
},
{
title: intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.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={false} {...layout} />
</Container>
</Spin>
);
};

View File

@ -20,12 +20,22 @@ import { useAsyncEffect } from 'ahooks';
import { Collapse, Form, Typography } from 'antd';
import { VerticalAlignBottomOutlined } from '@ant-design/icons';
import { getCertList } from '@/services/app';
import { CertUsingType } from '@/pages/app/AppConfig/constant';
import { CertUsingType } from '@/pages/app/AppDetail/constant';
import { useIntl } from '@umijs/max';
import Alert from '@/components/Alert';
import { createStyles } from 'antd-style';
import { ColProps } from 'antd/es/grid/col';
const IDP_ENCRYPT_CERT = 'idpEncryptCert';
const useStyles = createStyles(({ prefixCls }) => ({
alert: {
[`.${prefixCls}-alert-content .${prefixCls}-alert-description .${prefixCls}-form-item:last-child`]:
{
marginBottom: '0 !important',
},
},
}));
/**
*
*
@ -35,17 +45,30 @@ export default (props: {
appId: string;
protocolEndpoint: Record<string, string>;
collapsed?: boolean;
labelCol?: ColProps;
wrapperCol?: ColProps;
}) => {
const [configForm] = Form.useForm();
const { protocolEndpoint, appId, collapsed = true } = props;
const {
protocolEndpoint,
appId,
collapsed = true,
labelCol = {
span: 6,
},
wrapperCol = {
span: 12,
},
} = props;
const intl = useIntl();
const { styles } = useStyles();
useAsyncEffect(async () => {
configForm.setFieldsValue(protocolEndpoint);
}, [appId, protocolEndpoint]);
useAsyncEffect(async () => {
//获取SAML2签名证书
//获取JWT签名证书
const certResult = await getCertList(appId, CertUsingType.JWT_ENCRYPT);
if (certResult.success && certResult.result) {
certResult.result.forEach((value) => {
@ -75,8 +98,8 @@ export default (props: {
return (
<ProForm
layout={'horizontal'}
labelCol={{ xs: { span: 24 }, sm: { span: 6 } }}
wrapperCol={{ xs: { span: 24 }, sm: { span: 12 } }}
labelCol={labelCol}
wrapperCol={wrapperCol}
labelAlign={'right'}
labelWrap
submitter={false}
@ -92,22 +115,23 @@ export default (props: {
label: (
<a>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about',
})}
</a>
),
children: (
<Alert
type={'grey'}
className={styles.alert}
description={
<>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint',
})}
name={'idpSsoEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint.extra',
})}
readonly
proFieldProps={{
@ -119,7 +143,7 @@ export default (props: {
/>
<ProFormTextArea
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert',
})}
name={IDP_ENCRYPT_CERT}
disabled
@ -128,7 +152,7 @@ export default (props: {
<div style={{ display: 'inline-block' }}>
<div style={{ display: 'inline-block' }}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.0',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.0',
})}
</div>
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
@ -138,13 +162,13 @@ export default (props: {
>
<a>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.1',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.1',
})}
</a>
</Typography.Paragraph>
<a onClick={downloadEncryptCert}>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.2',
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.2',
})}
<VerticalAlignBottomOutlined />
</a>

View File

@ -0,0 +1,251 @@
/*
* 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';
import { Container } from '@/components/Container';
const layout = {
labelCol: {
span: 6,
},
wrapperCol: {
span: 12,
},
};
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}
banner
type={'info'}
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.detail.items.login_access.protocol_config.jwt',
})}
</a>{' '}
</span>
}
/>
<br />
<Container maxWidth={1152}>
<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.detail.items.login_access.protocol_config.jwt.redirect_url',
})}
name={'redirectUrl'}
fieldProps={{ allowClear: false }}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.redirect_url.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.redirect_url.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.jwt.target_link_url.extra',
})}
rules={[
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.target_link_url.rule.0.message',
}),
},
]}
/>
<ProFormRadio.Group
name={'bindingType'}
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.binding_type',
})}
initialValue={['post']}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.jwt.binding_type.rule.0.message',
}),
},
]}
/>
<ProFormSelect
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type',
})}
name={'idTokenSubjectType'}
options={[
{
value: 'user_id',
label: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0',
}),
},
{
value: 'app_user',
label: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1',
}),
},
]}
fieldProps={{ allowClear: false }}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message',
}),
},
]}
/>
<ProFormDigit
name={'idTokenTimeToLive'}
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_time_to_live',
})}
addonAfter={'秒'}
max={84600}
min={1}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.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={false}
{...layout}
/>
</Container>
</Spin>
);
};

View File

@ -20,7 +20,17 @@ import { useAsyncEffect } from 'ahooks';
import { Collapse, Form, Typography } from 'antd';
import { useIntl } from '@umijs/max';
import Alert from '@/components/Alert';
import { createStyles } from 'antd-style';
import { ColProps } from 'antd/es/grid/col';
const useStyles = createStyles(({ prefixCls }) => ({
alert: {
[`.${prefixCls}-alert-content .${prefixCls}-alert-description .${prefixCls}-form-item:last-child`]:
{
marginBottom: '0 !important',
},
},
}));
/**
*
*
@ -30,10 +40,24 @@ export default (props: {
appId: string;
protocolEndpoint: Record<string, string>;
collapsed?: boolean;
labelCol?: ColProps;
wrapperCol?: ColProps;
}) => {
const [configForm] = Form.useForm();
const { protocolEndpoint, appId, collapsed = true } = props;
const {
protocolEndpoint,
appId,
collapsed = true,
labelCol = {
span: 6,
},
wrapperCol = {
span: 12,
},
} = props;
const intl = useIntl();
const { styles } = useStyles();
useAsyncEffect(async () => {
configForm.setFieldsValue(protocolEndpoint);
}, [appId, protocolEndpoint]);
@ -41,8 +65,8 @@ export default (props: {
return (
<ProForm
layout={'horizontal'}
labelCol={{ xs: { span: 24 }, sm: { span: 6 } }}
wrapperCol={{ xs: { span: 24 }, sm: { span: 12 } }}
labelCol={labelCol}
wrapperCol={wrapperCol}
labelAlign={'right'}
submitter={false}
labelWrap
@ -58,20 +82,21 @@ export default (props: {
label: (
<a>
{intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.config_about',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.config_about',
})}
</a>
),
children: (
<Alert
type={'grey'}
className={styles.alert}
description={
<>
<ProFormText
label="Issuer"
name={'issuer'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.config_about.issuer.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.config_about.issuer.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -83,11 +108,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.discovery_endpoint',
})}
name={'discoveryEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.discovery_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.discovery_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -99,11 +124,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.authorization_endpoint',
})}
name={'authorizationEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.authorization_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.authorization_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -115,11 +140,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.token_endpoint',
})}
name={'tokenEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.token_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.token_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -131,7 +156,7 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.revoke_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.revoke_endpoint',
})}
name={'revokeEndpoint'}
proFieldProps={{
@ -144,11 +169,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.jwks_endpoint',
})}
name={'jwksEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.jwks_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.jwks_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -160,11 +185,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.userinfo_endpoint',
})}
name={'userinfoEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.userinfo_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.userinfo_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -176,11 +201,11 @@ export default (props: {
/>
<ProFormText
label={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.end_session_endpoint',
})}
name={'endSessionEndpoint'}
extra={intl.formatMessage({
id: 'pages.app.config.items.login_access.protocol_config.oidc.end_session_endpoint.extra',
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.end_session_endpoint.extra',
})}
proFieldProps={{
render: (value: string) => {
@ -188,7 +213,9 @@ export default (props: {
},
}}
readonly
fieldProps={{ autoComplete: 'off' }}
fieldProps={{
autoComplete: 'off',
}}
/>
</>
}

View File

@ -0,0 +1,642 @@
/*
* 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/AppDetail/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';
import { Container } from '@/components/Container';
const layout = {
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}
banner
type={'info'}
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.detail.items.login_access.protocol_config.oidc',
})}
</a>{' '}
</span>
}
/>
<br />
<Container maxWidth={1152}>
<ProForm
className={styles}
requiredMark={true}
layout={'horizontal'}
{...layout}
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.detail.items.login_access.protocol_config.oidc.auth_grant_types',
})}
layout={'vertical'}
name={'authGrantTypes'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.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.detail.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.detail.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.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0',
})}
</span>
<span style={{ color: '#999' }}>
{intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.require_proof_key',
})}
name={'requireProofKey'}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
}),
),
);
},
},
]}
>
{(fields, { add, remove }, { errors }) => (
<>
{fields.map((field, index) => (
<Form.Item
{...(index === 0 ? layout : formItemLayoutWithOutLabel)}
required={true}
key={field.key}
label={
index === 0
? intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message',
}),
},
]}
noStyle
>
<Input
placeholder={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.placeholder',
})}
/>
</Form.Item>
<DeleteOutlined onClick={() => remove(field.name)} />
</div>
</Form.Item>
))}
<Form.Item
{...(fields.length === 0 ? layout : formItemLayoutWithOutLabel)}
required={true}
label={
fields.length === 0
? intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris',
})
: ''
}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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 ? layout : formItemLayoutWithOutLabel)}
key={field.key}
label={
index === 0
? intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
}),
},
{
type: 'url',
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message',
}),
},
]}
noStyle
>
<Input
placeholder={intl.formatMessage({
id: 'pages.app.config.detail.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 ? layout : formItemLayoutWithOutLabel)}
label={
fields.length === 0
? intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris',
})
: ''
}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.init_login_type',
})}
name={'initLoginType'}
allowClear={false}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.rule.0.message',
}),
},
]}
options={[
{
value: SsoInitiator.APP,
label: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.option.0',
}),
},
{
value: SsoInitiator.PORTAL_OR_APP,
label: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.option.1',
}),
},
]}
extra={
<>
<span>
{intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.extra.0',
})}
</span>
<br />
<span>
{intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.init_login_url',
})}
name={'initLoginUrl'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url.rule.0.message',
}),
},
]}
fieldProps={{
placeholder: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url.field_props',
}),
}}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.advanced',
})}
</a>
),
children: (
<>
<ProFormCheckbox.Group
label={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes',
})}
layout={'vertical'}
name={'grantScopes'}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message',
}),
},
]}
extra={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.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.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label',
})}
</span>
</>
),
},
]}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra',
})}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra',
})}
/>
<ProFormDigit
label={intl.formatMessage({
id: 'pages.app.config.detail.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.detail.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.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm',
})}
name={'idTokenSignatureAlgorithm'}
allowClear={false}
extra={intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra',
})}
rules={[
{
required: true,
message: intl.formatMessage({
id: 'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message',
}),
},
]}
/>
</>
),
},
]}
/>
</ProForm>
<Divider style={{ margin: 0 }} />
<ConfigAbout appId={id} protocolEndpoint={protocolEndpoint} collapsed={false} {...layout} />
</Container>
</Spin>
);
};

View File

@ -19,22 +19,14 @@
* ConfigTabs
*/
export enum ConfigTabs {
//基本信息
basic = 'basic',
//应用配置
app_config = 'app_config',
//协议
protocol_config = 'protocol_config',
//账户同步
account_sync = 'account_sync',
//访问授权
access_policy = 'access_policy',
//登录访问
login_access = 'login_access',
//应用权限
app_permission = 'app_permission',
//资源管理
permission_resource = 'permission_resource',
//角色管理
permission_role = 'permission_role',
//应用账户
app_account = 'app_account',
}
@ -63,7 +55,6 @@ export enum SsoScope {
* 使
*/
export enum CertUsingType {
SAML_SIGN = 'saml_sign',
JWT_ENCRYPT = 'jwt_encrypt',
}

View File

@ -0,0 +1,42 @@
/*
* 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 type GetApp = {
id: string;
type: string;
name: string;
icon: string;
template: string;
protocol: string;
protocolName: string;
clientId: string;
clientSecret: string;
//sso发起方
initLoginType: string;
//sso登录链接
initLoginUrl: string;
nameIdValueType: string;
//授权范围
authorizationType: string;
enabled: boolean;
remark: string;
groupIds: string[];
};

View File

@ -15,6 +15,6 @@
* 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 AppBasic from './AppBasic';
import AppDetail from './AppDetail';
export default AppBasic;
export default AppDetail;

View File

@ -0,0 +1,355 @@
/*
* 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.detail.error': '未选择应用',
'pages.app.config.detail.extra.delete': '删除应用',
'pages.app.config.detail.extra.delete.confirm_title': '您确定要删除此应用?',
'pages.app.config.detail.extra.delete.confirm_content': '此操作不可恢复,请谨慎操作!',
'pages.app.config.detail.config': '应用配置',
'pages.app.config.detail.config.name': '应用名称',
'pages.app.config.detail.config.name.rule.0.message': '应用名称为必填项',
'pages.app.config.detail.config.icon': '应用图标',
'pages.app.config.detail.config.icon.rule.0.message': '应用图标为必填项',
'pages.app.config.detail.config.icon.rule.1.message': '应用图标上传失败',
'pages.app.config.detail.config.icon.desc.1': '必须为 PNG/JPG 格式',
'pages.app.config.detail.config.icon.desc.2': '建议使用 256 * 256 像素方形图标',
'pages.app.config.detail.config.enabled': '应用状态',
'pages.app.config.detail.config.type': '应用类型',
'pages.app.config.detail.config.group': '应用分组',
'pages.app.config.detail.config.type.value_enum.custom_made': '定制应用',
'pages.app.config.detail.config.type.value_enum.standard': '标准应用',
'pages.app.config.detail.config.type.value_enum.self_developed': '自研应用',
'pages.app.config.detail.config.protocol_name': '应用协议',
'pages.app.config.detail.config.client_id': '客户端 ID',
'pages.app.config.detail.config.client_secret': '客户端秘钥',
'pages.app.config.detail.config.create_time': '创建时间',
'pages.app.config.detail.config.remark': '备注',
'pages.app.config.detail.items.login_access': '协议配置',
'pages.app.config.detail.items.login_access.protocol_config': '单点登录',
'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type':
'授权范围',
'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.extra':
'若选择手动授权,需要在 访问授权 中进行权限分配。',
'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.rule.0.message':
'请选择授权范围',
'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.option.0':
'手动授权',
'pages.app.config.detail.items.login_access.protocol_config.common.authorization_type.option.1':
'全员可访问',
'pages.app.config.detail.items.login_access.protocol_config.form': '表单代填单点登录',
'pages.app.config.detail.items.login_access.protocol_config.form.login_url': '登录提交URL',
'pages.app.config.detail.items.login_access.protocol_config.form.login_url.placeholder':
'请输入登录提交URL',
'pages.app.config.detail.items.login_access.protocol_config.form.login_url.extra':
'登录表单提交完整URL以http://或https://开头https://oa.xxxx.com/login',
'pages.app.config.detail.items.login_access.protocol_config.form.login_url.rule.0.message':
'应用登录URL为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.login_url.rule.1.message':
'URL格式不正确',
'pages.app.config.detail.items.login_access.protocol_config.form.username_field':
'登录名属性名称',
'pages.app.config.detail.items.login_access.protocol_config.form.username_field.placeholder':
'请输入登录名属性名称',
'pages.app.config.detail.items.login_access.protocol_config.form.username_field.extra':
'username标签的name属性',
'pages.app.config.detail.items.login_access.protocol_config.form.username_field.rule.0.message':
'登录名属性名称为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.password_field':
'登录密码属性名称',
'pages.app.config.detail.items.login_access.protocol_config.form.password_field.placeholder':
'请输入登录密码属性名称',
'pages.app.config.detail.items.login_access.protocol_config.form.password_field.extra':
'password标签的name属性',
'pages.app.config.detail.items.login_access.protocol_config.form.password_field.rule.0.message':
'登录密码属性名称为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_type':
'登录密码加密算法',
'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_type.extra':
'登录密码加密算法类型。',
'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_key':
'登录密码加密秘钥',
'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_key.extra':
'登录密码加密秘钥。',
'pages.app.config.detail.items.login_access.protocol_config.form.password_encrypt_key.rule.0.message':
'登录密码加密秘钥为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_type':
'用户名加密算法',
'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_type.extra':
'用户名加密算法类型。',
'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_key':
'用户名加密秘钥',
'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_key.extra':
'用户名加密秘钥。',
'pages.app.config.detail.items.login_access.protocol_config.form.username_encrypt_key.rule.0.message':
'用户名加密秘钥为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.submit_type': '登录提交方式',
'pages.app.config.detail.items.login_access.protocol_config.form.submit_type.rule.0.message':
'登录提交方式为必选项',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field': '登录其他字段',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.columns.field_name':
'属性名称',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.columns.field_name.rule.0':
'此项为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.columns.field_value':
'属性值',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.columns.field_value.rule.0':
'此项为必填项',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.columns.option':
'操作',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.record_creator_props':
'添加其他字段',
'pages.app.config.detail.items.login_access.protocol_config.form.other_field.editable':
'您确定要删除此属性吗?',
'pages.app.config.detail.items.login_access.protocol_config.form.config_about': '应用配置信息',
'pages.app.config.detail.items.login_access.protocol_config.form.config_about.idp_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.detail.items.login_access.protocol_config.form.config_about.idp_sso_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.detail.items.login_access.protocol_config.jwt': 'JWT单点登录',
'pages.app.config.detail.items.login_access.protocol_config.jwt.redirect_url': 'SSO 地址',
'pages.app.config.detail.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.detail.items.login_access.protocol_config.jwt.redirect_url.rule.0.message':
'请输入JWT应用SSO地址',
'pages.app.config.detail.items.login_access.protocol_config.jwt.redirect_url.rule.1.message':
'JWT应用SSO地址格式不正确',
'pages.app.config.detail.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.detail.items.login_access.protocol_config.jwt.target_link_url.rule.0.message':
'地址格式不正确',
'pages.app.config.detail.items.login_access.protocol_config.jwt.binding_type': 'SSO 绑定类型',
'pages.app.config.detail.items.login_access.protocol_config.jwt.binding_type.extra':
'指定向JWT应用发送 id_token 的请求方式。',
'pages.app.config.detail.items.login_access.protocol_config.jwt.binding_type.rule.0.message':
'登录提交方式为必选项',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type':
'id_token 主体类型',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.option.0':
'用户ID',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.option.1':
'应用账户',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.extra':
'id_token 中 sub 主体(用户) 类型',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_subject_type.rule.0.message':
'请选择 CAS 用户标识',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_time_to_live':
'id_token 过期时间',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_time_to_live.extra':
'id_token 的有效期单位为秒。可设置范围为1-84600。',
'pages.app.config.detail.items.login_access.protocol_config.jwt.idtoken_time_to_live.rule.0.message':
'请配置 id_token 的有效期',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about': '应用配置信息',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint':
'IdP SSO 地址',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_sso_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint':
'登出端点',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_slo_endpoint.extra':
'应用发起单点登出的地址。',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert':
'JWT 验签公钥',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.0':
'下载或复制证书,并导入或粘贴到应用中。',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.1':
'复制证书内容',
'pages.app.config.detail.items.login_access.protocol_config.jwt.config_about.idp_encrypt_cert.extra.2':
'下载证书 .cer 文件',
'pages.app.config.detail.items.login_access.protocol_config.oidc': 'OIDC 单点登录',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types': '授权模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.rule.0.message':
'请勾选授权模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.0':
'授权码模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.authorization_code.label.1':
' authorization_code 模式,用于账户的登录认证、授权。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.0':
'令牌刷新模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.refresh_token.label.1':
'refresh_token 模式,用于既有 token 的延期。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.0':
'隐式模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.implicit.label.1':
' implicit\n' +
' 模式,由于协议本身的安全性,通常不推荐使用。如果有特殊需求,可以使用变体的' +
' PKCE 的授权码模式。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.0':
'密码模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.password.label.1':
'password 模式,由于协议本身的安全性,通常不推荐使用。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.0':
'设备模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.auth_grant_types.option.device.label.1':
'device 模式,兼容设备发起的登录流程。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.require_proof_key':
'PKCE用于授权码模式',
'pages.app.config.detail.items.login_access.protocol_config.oidc.require_proof_key.extra':
'PKCE Proof Key for Code Exchange是 OAuth 2.0 的安全性扩展模式,用于防护 CSRF、中间人进攻等恶意攻击。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris':
'登录 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.extra':
'Redirect URI 白名单,应用在请求登录时携带 redirect_uri 参数该值需要在白名单中IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.rule.0.message':
'请配置登录 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.rule.1.message':
'Redirect URI 格式不正确',
'pages.app.config.detail.items.login_access.protocol_config.oidc.redirect_uris.placeholder':
'请输入登录 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris':
'登出 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris.extra':
'登出 Redirect URI 白名单,应用在请求登录时携带 post_logout_redirect_uri 参数该值需要在白名单中IAM 才会在认证完成后发起跳转。若有多条,请点击添加进行扩展',
'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.0.message':
'请配置登出 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris.rule.1.message':
'登出 Redirect URI 格式不正确',
'pages.app.config.detail.items.login_access.protocol_config.oidc.post_logout_redirect_uris.placeholder':
'请输入登出 Redirect URI',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type': 'SSO 发起方',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.rule.0.message':
'请配置 SSO 发起方',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.option.0':
'只允许应用发起',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.option.1':
'支持门户和应用发起',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.extra.0':
'门户发起:由 IAM 门户页点击进行 SSO 。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_type.extra.1':
'应用发起:由应用登录主动发起。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url': '登录发起地址',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url.rule.0.message':
'登录发起地址不能为空',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url.field_props':
'请输入登录链接',
'pages.app.config.detail.items.login_access.protocol_config.oidc.init_login_url.extra':
'若您希望由 TopIAM 门户页访问应用,请填写 TopIAM 发起 SSO 请求访问的应用地址。该地址接收到请求,应即刻转向 TopIAM / authorize 授权端点。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced': '显示高级配置',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes':
'用户信息范围',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.rule.0.message':
'请勾选用户信息范围',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.extra':
'用户登录后,使用用户信息端点或解析 id_token 可以获取到的已登录用户信息 。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.1.label':
'应用可获取登录用户邮箱信息。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.2.label':
'应用可获取登录用户手机信息。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.grant_scopes.option.3.label':
'应用可获取登录用户详情信息。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live':
'access_token 有效期',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.access_token_time_to_live.extra':
'access_token 用于请求 IAM 接口,过期后需要使用 refresh_token 刷新,或重新登录。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live':
'refresh_token 有效期',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.refresh_token_time_to_live.extra':
'用于获取新的 access_token 和 id_tokenrefresh_token 过期后,用户需要重新登录。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live':
'id_token 有效期',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_time_to_live.extra':
'id_token 用于鉴别用户身份JWT格式允许应用使用公钥自行验证用户身份。最小5分钟最大24小时过期后需要使用refresh_token 刷新,或重新登录。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm':
'id_token 签名算法',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.extra':
'id_token 签名使用的非对称算法。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.advanced.idtoken_signature_algorithm.rule.0.message':
'请配置 id_token 签名算法',
'pages.app.config.detail.items.login_access.protocol_config.oidc.config_about': '应用配置信息',
'pages.app.config.detail.items.login_access.protocol_config.oidc.config_about.issuer.extra':
'用于标识 token 发放来源的字段。同时是下述接口的 baseUrl。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.discovery_endpoint': '发现端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.discovery_endpoint.extra':
'用于获取当前 IAM 支持的各端点信息和支持的模式、参数信息,可公开访问。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.authorization_endpoint':
'授权端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.authorization_endpoint.extra':
'应用发起单点登录的地址。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.token_endpoint': '令牌端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.token_endpoint.extra':
'应用在单点登录过程中,拿到 code 后,从后端发起换取 token 的接口地址。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.revoke_endpoint': '令牌吊销端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.jwks_endpoint': '验签公钥端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.jwks_endpoint.extra':
'用于验证 id_token、完成 SSO 流程的公钥端点。公钥可能会轮转。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.userinfo_endpoint':
'用户信息端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.userinfo_endpoint.extra':
'在账户登录后,使用 access_token 调用用户信息端点,获取账户基本信息。',
'pages.app.config.detail.items.login_access.protocol_config.oidc.end_session_endpoint':
'结束会话端点',
'pages.app.config.detail.items.login_access.protocol_config.oidc.end_session_endpoint.extra':
'结束会话端点可用于触发单点注销。',
'pages.app.config.detail.items.login_access.app_account': '应用账户',
'pages.app.config.detail.items.login_access.app_account.columns.username': '系统用户',
'pages.app.config.detail.items.login_access.app_account.columns.account': '应用账户',
'pages.app.config.detail.items.login_access.app_account.columns.create_time': '添加时间',
'pages.app.config.detail.items.login_access.app_account.columns.option': '操作',
'pages.app.config.detail.items.login_access.app_account.columns.option.popconfirm.title':
'您确定要删除此应用账户?',
'pages.app.config.detail.items.login_access.app_account.create_app_account': '添加应用账户',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.user_id.rule.0.message':
'请选择系统用户',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.user_id.placeholder':
'请输入用户名、手机或邮箱搜索用户',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.account':
'应用用户名',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.account.rule.0.message':
'请输入账户访问应用时所使用户名',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.password':
'应用用户密码',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.password.rule.0.message':
'请输入账户访问应用时所使密码',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.app_identity':
'应用身份',
'pages.app.config.detail.items.login_access.app_account.create_app_account.modal_form.app_identity.rule.0.message':
'请输入账户访问应用时所使身份',
'pages.app.config.detail.items.login_access.access_policy': '访问授权',
'pages.app.config.detail.items.login_access.access_policy.columns.subject_name': '授权主体',
'pages.app.config.detail.items.login_access.access_policy.columns.subject_type': '主体类型',
'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user':
'用户',
'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.user_group':
'用户组',
'pages.app.config.detail.items.login_access.access_policy.columns.subject_type.value_enum.organization':
'组织机构',
'pages.app.config.detail.items.login_access.access_policy.columns.create_time': '授权时间',
'pages.app.config.detail.items.login_access.access_policy.columns.option': '操作',
'pages.app.config.detail.items.login_access.access_policy.columns.option.popconfirm.title':
'您确定要取消主体授权?取消授权后不可自动恢复。',
'pages.app.config.detail.items.login_access.access_policy.create_policy': '添加授权',
'pages.app.config.detail.items.login_access.access_policy.cancel_policy': '取消授权',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type':
'授权类型',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.rule.0.message':
'请选择授权类型',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user':
'授权用户',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.rule.0.message':
'请选择授权用户',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user.placeholder':
'请输入用户名搜索用户',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group':
'授权分组',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_user_group.rule.0.message':
'请选择授权分组',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization':
'授权组织',
'pages.app.config.detail.items.login_access.access_policy.create_policy.modal_form.subject_type.auth_organization.rule.0.message':
'请选择组织节点',
};

View File

@ -0,0 +1,31 @@
/*
* 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 { request } from '@@/exports';
import {
GetApp,
} from './data.d';
/**
* Get Application
*/
export async function getApp(id: string): Promise<API.ApiResult<GetApp>> {
return request<API.ApiResult<GetApp>>(`/api/v1/app/get/${id}`, {
method: 'GET',
});
}