mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
feat(console,phrases): implement application permissions assignment 4-4 (#5230)
feat(console): implement application permission assignment modal implement application permission assignment modal
This commit is contained in:
parent
beef9c0657
commit
6b49a2e2e3
26 changed files with 697 additions and 76 deletions
|
@ -28,6 +28,7 @@ type Props<TFieldValues extends FieldValues, TName extends FieldPath<TFieldValue
|
|||
};
|
||||
isLoading?: boolean;
|
||||
onAdd?: () => void;
|
||||
errorMessage?: string;
|
||||
};
|
||||
|
||||
export const pageSize = 10;
|
||||
|
@ -48,6 +49,7 @@ function TemplateTable<
|
|||
pagination,
|
||||
isLoading,
|
||||
onAdd,
|
||||
errorMessage,
|
||||
}: Props<TFieldValues, TName>) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
|
@ -63,7 +65,7 @@ function TemplateTable<
|
|||
<DynamicT forKey={name} interpolation={{ count: 2 }} />
|
||||
</header>
|
||||
)}
|
||||
{onAdd && noData && (
|
||||
{onAdd && noData && !errorMessage && (
|
||||
<>
|
||||
{name && (
|
||||
<div className={styles.empty}>
|
||||
|
@ -73,6 +75,7 @@ function TemplateTable<
|
|||
<Button icon={<Plus />} title="general.add" onClick={onAdd} />
|
||||
</>
|
||||
)}
|
||||
{noData && errorMessage && <div className={styles.empty}>{errorMessage}</div>}
|
||||
{!noData && (
|
||||
<Table
|
||||
hasBorder
|
||||
|
|
|
@ -22,7 +22,7 @@ import { type DataEntry, type DataGroup, type SelectedDataEntry } from './type';
|
|||
* @param availableDataGroups - The list of available data groups. (Single level tree form)
|
||||
*/
|
||||
|
||||
type Props<TEntry extends DataEntry> = {
|
||||
export type Props<TEntry extends DataEntry> = {
|
||||
title: AdminConsoleKey | ReactElement<typeof DangerousRaw>;
|
||||
selectedData: Array<SelectedDataEntry<TEntry>>;
|
||||
setSelectedData: (dataList: Array<SelectedDataEntry<TEntry>>) => void;
|
||||
|
@ -61,5 +61,4 @@ function DataTransferBox<TEntry extends DataEntry = DataEntry>({
|
|||
);
|
||||
}
|
||||
|
||||
// eslint-disable-next-line import/no-unused-modules -- will be used in the following PR
|
||||
export default DataTransferBox;
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import { ApplicationUserConsentScopeType } from '@logto/schemas';
|
||||
|
||||
export const permissionTabs = Object.freeze({
|
||||
[ApplicationUserConsentScopeType.UserScopes]: {
|
||||
title: 'application_details.permissions.user_profile',
|
||||
key: ApplicationUserConsentScopeType.UserScopes,
|
||||
},
|
||||
[ApplicationUserConsentScopeType.ResourceScopes]: {
|
||||
title: 'application_details.permissions.api_resource',
|
||||
key: ApplicationUserConsentScopeType.ResourceScopes,
|
||||
},
|
||||
[ApplicationUserConsentScopeType.OrganizationScopes]: {
|
||||
title: 'application_details.permissions.organization',
|
||||
key: ApplicationUserConsentScopeType.OrganizationScopes,
|
||||
},
|
||||
}) satisfies {
|
||||
[key in ApplicationUserConsentScopeType]: {
|
||||
title: AdminConsoleKey;
|
||||
key: key;
|
||||
};
|
||||
};
|
|
@ -0,0 +1,99 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import { ApplicationUserConsentScopeType } from '@logto/schemas';
|
||||
import { useCallback } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import ConfirmModal from '@/ds-components/ConfirmModal';
|
||||
import DataTransferBox from '@/ds-components/DataTransferBox';
|
||||
import TabNav, { TabNavItem } from '@/ds-components/TabNav';
|
||||
import TabWrapper from '@/ds-components/TabWrapper';
|
||||
|
||||
import { permissionTabs } from './constants';
|
||||
import useApplicationScopesAssignment from './use-application-scopes-assignment';
|
||||
|
||||
const modalText = Object.freeze({
|
||||
title: 'application_details.permissions.table_name',
|
||||
subtitle: 'application_details.permissions.permissions_assignment_description',
|
||||
saveBtn: 'general.save',
|
||||
}) satisfies Record<string, AdminConsoleKey>;
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
applicationId: string;
|
||||
};
|
||||
|
||||
function ApplicationScopesAssignmentModal({ isOpen, onClose, applicationId }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const { activeTab, setActiveTab, scopesAssignment, clearSelectedData, onSubmit, isLoading } =
|
||||
useApplicationScopesAssignment(applicationId);
|
||||
|
||||
const onCloseHandler = useCallback(() => {
|
||||
onClose();
|
||||
clearSelectedData();
|
||||
setActiveTab(ApplicationUserConsentScopeType.UserScopes);
|
||||
}, [clearSelectedData, onClose, setActiveTab]);
|
||||
|
||||
const onSubmitHandler = useCallback(async () => {
|
||||
await onSubmit();
|
||||
onCloseHandler();
|
||||
}, [onCloseHandler, onSubmit]);
|
||||
|
||||
// If any of the tabs has selected scopes, the modal is dirty
|
||||
const isDirty = Object.values(scopesAssignment).some(
|
||||
({ selectedData }) => selectedData.length > 0
|
||||
);
|
||||
|
||||
return (
|
||||
<ConfirmModal
|
||||
isOpen={isOpen}
|
||||
isLoading={isLoading}
|
||||
title={modalText.title}
|
||||
subtitle={modalText.subtitle}
|
||||
isConfirmButtonDisabled={!isDirty}
|
||||
confirmButtonType="primary"
|
||||
confirmButtonText={modalText.saveBtn}
|
||||
isCancelButtonVisible={false}
|
||||
size="large"
|
||||
onCancel={onCloseHandler}
|
||||
onConfirm={onSubmitHandler}
|
||||
>
|
||||
<TabNav>
|
||||
{Object.values(permissionTabs).map(({ title, key }) => (
|
||||
<TabNavItem
|
||||
key={key}
|
||||
isActive={key === activeTab}
|
||||
onClick={() => {
|
||||
setActiveTab(key);
|
||||
}}
|
||||
>
|
||||
{`${t(title)}(${scopesAssignment[key].selectedData.length})`}
|
||||
</TabNavItem>
|
||||
))}
|
||||
</TabNav>
|
||||
<TabWrapper
|
||||
key={ApplicationUserConsentScopeType.UserScopes}
|
||||
isActive={ApplicationUserConsentScopeType.UserScopes === activeTab}
|
||||
>
|
||||
<DataTransferBox {...scopesAssignment[ApplicationUserConsentScopeType.UserScopes]} />
|
||||
</TabWrapper>
|
||||
<TabWrapper
|
||||
key={ApplicationUserConsentScopeType.ResourceScopes}
|
||||
isActive={ApplicationUserConsentScopeType.ResourceScopes === activeTab}
|
||||
>
|
||||
<DataTransferBox {...scopesAssignment[ApplicationUserConsentScopeType.ResourceScopes]} />
|
||||
</TabWrapper>
|
||||
<TabWrapper
|
||||
key={ApplicationUserConsentScopeType.OrganizationScopes}
|
||||
isActive={ApplicationUserConsentScopeType.OrganizationScopes === activeTab}
|
||||
>
|
||||
<DataTransferBox
|
||||
{...scopesAssignment[ApplicationUserConsentScopeType.OrganizationScopes]}
|
||||
/>
|
||||
</TabWrapper>
|
||||
</ConfirmModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default ApplicationScopesAssignmentModal;
|
|
@ -0,0 +1,29 @@
|
|||
import {
|
||||
type ApplicationUserConsentScopesResponse,
|
||||
type ApplicationUserConsentScopeType,
|
||||
} from '@logto/schemas';
|
||||
|
||||
import { type Props as DataTransferBoxProps } from '@/ds-components/DataTransferBox';
|
||||
import { type DataEntry } from '@/ds-components/DataTransferBox/type';
|
||||
|
||||
type DataTransferBoxComponentProps<V extends DataEntry> = Pick<
|
||||
DataTransferBoxProps<V>,
|
||||
'selectedData' | 'setSelectedData' | 'availableDataList' | 'availableDataGroups' | 'title'
|
||||
>;
|
||||
|
||||
type ScopeAssignmentHookReturnType<
|
||||
T extends ApplicationUserConsentScopeType,
|
||||
V extends DataEntry,
|
||||
> = {
|
||||
scopeType: T;
|
||||
} & DataTransferBoxComponentProps<V>;
|
||||
|
||||
// This is used to parse the ApplicationUserConsentScopeType to the response key
|
||||
type CamelCase<T> = T extends `${infer A}-${infer B}` ? `${A}${Capitalize<CamelCase<B>>}` : T;
|
||||
|
||||
export type ScopeAssignmentHook<
|
||||
T extends ApplicationUserConsentScopeType,
|
||||
V extends DataEntry = DataEntry,
|
||||
> = (
|
||||
assignedScopes?: ApplicationUserConsentScopesResponse[CamelCase<T>]
|
||||
) => ScopeAssignmentHookReturnType<T, V>;
|
|
@ -0,0 +1,89 @@
|
|||
import {
|
||||
ApplicationUserConsentScopeType,
|
||||
type ApplicationUserConsentScopesResponse,
|
||||
} from '@logto/schemas';
|
||||
import { conditional } from '@silverhand/essentials';
|
||||
import { useState, useCallback, useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import useApi, { type RequestError } from '@/hooks/use-api';
|
||||
|
||||
import useOrganizationScopesAssignment from './use-organization-scopes-assignment';
|
||||
import useResourceScopesAssignment from './use-resource-scopes-assignment';
|
||||
import useUserScopesAssignment from './use-user-scopes-assignment';
|
||||
|
||||
const useApplicationScopesAssignment = (applicationId: string) => {
|
||||
const [activeTab, setActiveTab] = useState<ApplicationUserConsentScopeType>(
|
||||
ApplicationUserConsentScopeType.UserScopes
|
||||
);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const { data, mutate } = useSWR<ApplicationUserConsentScopesResponse, RequestError>(
|
||||
`api/applications/${applicationId}/user-consent-scopes`
|
||||
);
|
||||
|
||||
const userScopesAssignment = useUserScopesAssignment(data?.userScopes);
|
||||
const organizationScopesAssignment = useOrganizationScopesAssignment(data?.organizationScopes);
|
||||
const resourceScopesAssignment = useResourceScopesAssignment(data?.resourceScopes);
|
||||
|
||||
const clearSelectedData = useCallback(() => {
|
||||
userScopesAssignment.setSelectedData([]);
|
||||
organizationScopesAssignment.setSelectedData([]);
|
||||
resourceScopesAssignment.setSelectedData([]);
|
||||
}, [organizationScopesAssignment, resourceScopesAssignment, userScopesAssignment]);
|
||||
|
||||
const onSubmit = useCallback(async () => {
|
||||
setIsLoading(true);
|
||||
const newUserScopes = userScopesAssignment.selectedData.map(({ id }) => id);
|
||||
const newOrganizationScopes = organizationScopesAssignment.selectedData.map(({ id }) => id);
|
||||
const newResourceScopes = resourceScopesAssignment.selectedData.map(({ id }) => id);
|
||||
|
||||
await api
|
||||
.post(`api/applications/${applicationId}/user-consent-scopes`, {
|
||||
json: {
|
||||
...conditional(newUserScopes.length > 0 && { userScopes: newUserScopes }),
|
||||
...conditional(
|
||||
newOrganizationScopes.length > 0 && {
|
||||
organizationScopes: newOrganizationScopes,
|
||||
}
|
||||
),
|
||||
...conditional(newResourceScopes.length > 0 && { resourceScopes: newResourceScopes }),
|
||||
},
|
||||
})
|
||||
.finally(() => {
|
||||
setIsLoading(false);
|
||||
});
|
||||
|
||||
void mutate();
|
||||
}, [
|
||||
api,
|
||||
applicationId,
|
||||
mutate,
|
||||
organizationScopesAssignment.selectedData,
|
||||
resourceScopesAssignment.selectedData,
|
||||
userScopesAssignment.selectedData,
|
||||
]);
|
||||
|
||||
// Return selectedScopes and setSelectedScopes based on the active tab
|
||||
const scopesAssignment = useMemo(
|
||||
() => ({
|
||||
[ApplicationUserConsentScopeType.UserScopes]: userScopesAssignment,
|
||||
[ApplicationUserConsentScopeType.OrganizationScopes]: organizationScopesAssignment,
|
||||
[ApplicationUserConsentScopeType.ResourceScopes]: resourceScopesAssignment,
|
||||
}),
|
||||
[organizationScopesAssignment, resourceScopesAssignment, userScopesAssignment]
|
||||
);
|
||||
|
||||
return {
|
||||
isLoading,
|
||||
activeTab,
|
||||
setActiveTab,
|
||||
scopesAssignment,
|
||||
clearSelectedData,
|
||||
onSubmit,
|
||||
};
|
||||
};
|
||||
|
||||
export default useApplicationScopesAssignment;
|
|
@ -0,0 +1,44 @@
|
|||
import {
|
||||
ApplicationUserConsentScopeType,
|
||||
type OrganizationScope,
|
||||
type ApplicationUserConsentScopesResponse,
|
||||
} from '@logto/schemas';
|
||||
import { useState, useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
|
||||
import { type ScopeAssignmentHook } from './type';
|
||||
|
||||
type HookType = ScopeAssignmentHook<
|
||||
ApplicationUserConsentScopeType.OrganizationScopes,
|
||||
ApplicationUserConsentScopesResponse['organizationScopes'][number]
|
||||
>;
|
||||
|
||||
type SelectedDataType = ReturnType<HookType>['selectedData'][number];
|
||||
|
||||
const useOrganizationScopesAssignment: HookType = (assignedOrganizationScopes = []) => {
|
||||
const [selectedData, setSelectedData] = useState<SelectedDataType[]>([]);
|
||||
|
||||
const { data: organizationScopes } = useSWR<OrganizationScope[], RequestError>(
|
||||
'api/organization-scopes'
|
||||
);
|
||||
|
||||
const availableDataList = useMemo(
|
||||
() =>
|
||||
(organizationScopes ?? []).filter(
|
||||
({ id }) => !assignedOrganizationScopes.some((scope) => scope.id === id)
|
||||
),
|
||||
[organizationScopes, assignedOrganizationScopes]
|
||||
);
|
||||
|
||||
return {
|
||||
scopeType: ApplicationUserConsentScopeType.OrganizationScopes,
|
||||
selectedData,
|
||||
setSelectedData,
|
||||
availableDataList,
|
||||
title: 'application_details.permissions.organization_permissions_assignment_form_title',
|
||||
};
|
||||
};
|
||||
|
||||
export default useOrganizationScopesAssignment;
|
|
@ -0,0 +1,69 @@
|
|||
import {
|
||||
type ResourceResponse,
|
||||
ApplicationUserConsentScopeType,
|
||||
type ApplicationUserConsentScopesResponse,
|
||||
} from '@logto/schemas';
|
||||
import { isManagementApi } from '@logto/schemas';
|
||||
import { useState, useMemo } from 'react';
|
||||
import useSWR from 'swr';
|
||||
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
|
||||
import { type ScopeAssignmentHook } from './type';
|
||||
|
||||
type HookType = ScopeAssignmentHook<
|
||||
ApplicationUserConsentScopeType.ResourceScopes,
|
||||
ApplicationUserConsentScopesResponse['resourceScopes'][number]['scopes'][number]
|
||||
>;
|
||||
|
||||
type SelectedDataType = ReturnType<HookType>['selectedData'][number];
|
||||
|
||||
const useResourceScopesAssignment: HookType = (assignedResourceScopes) => {
|
||||
const [selectedData, setSelectedData] = useState<SelectedDataType[]>([]);
|
||||
|
||||
const { data: allResources } = useSWR<ResourceResponse[], RequestError>(
|
||||
'api/resources?includeScopes=true'
|
||||
);
|
||||
|
||||
const availableDataGroups = useMemo(() => {
|
||||
if (!allResources) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const resourcesWithScopes: ReturnType<HookType>['availableDataGroups'] = allResources
|
||||
// Filter out the management APIs
|
||||
.filter((resource) => !isManagementApi(resource.indicator))
|
||||
.map(({ name, scopes, id }) => {
|
||||
const assignedResource = assignedResourceScopes?.find(({ resource }) => resource.id === id);
|
||||
|
||||
return {
|
||||
groupId: id,
|
||||
groupName: name,
|
||||
dataList: scopes
|
||||
// Filter out the scopes that have been assigned
|
||||
.filter(({ id: scopeId }) => {
|
||||
if (!assignedResourceScopes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return assignedResource
|
||||
? !assignedResource.scopes.some((scope) => scope.id === scopeId)
|
||||
: true;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
// Filter out the resources that have no scopes
|
||||
return resourcesWithScopes.filter(({ dataList }) => dataList.length > 0);
|
||||
}, [allResources, assignedResourceScopes]);
|
||||
|
||||
return {
|
||||
scopeType: ApplicationUserConsentScopeType.ResourceScopes,
|
||||
selectedData,
|
||||
setSelectedData,
|
||||
availableDataGroups,
|
||||
title: 'application_details.permissions.api_resource_permissions_assignment_form_title',
|
||||
};
|
||||
};
|
||||
|
||||
export default useResourceScopesAssignment;
|
|
@ -0,0 +1,34 @@
|
|||
import { UserScope } from '@logto/core-kit';
|
||||
import { ApplicationUserConsentScopeType } from '@logto/schemas';
|
||||
import { useState, useMemo } from 'react';
|
||||
|
||||
import { type ScopeAssignmentHook } from './type';
|
||||
|
||||
type HookType = ScopeAssignmentHook<ApplicationUserConsentScopeType.UserScopes>;
|
||||
type SelectedDataType = ReturnType<HookType>['selectedData'][number];
|
||||
|
||||
const useUserScopesAssignment: HookType = (assignedUserScopes = []) => {
|
||||
const [selectedData, setSelectedData] = useState<SelectedDataType[]>([]);
|
||||
|
||||
const availableDataList = useMemo(
|
||||
() =>
|
||||
Object.values(UserScope)
|
||||
.map((name) => ({
|
||||
name,
|
||||
id: name,
|
||||
}))
|
||||
// Filter out the scopes that have been assigned
|
||||
.filter(({ id }) => !assignedUserScopes.includes(id)),
|
||||
[assignedUserScopes]
|
||||
);
|
||||
|
||||
return {
|
||||
scopeType: ApplicationUserConsentScopeType.UserScopes,
|
||||
selectedData,
|
||||
setSelectedData,
|
||||
availableDataList,
|
||||
title: 'application_details.permissions.user_permissions_assignment_form_title',
|
||||
};
|
||||
};
|
||||
|
||||
export default useUserScopesAssignment;
|
|
@ -10,8 +10,9 @@ import TemplateTable from '@/components/TemplateTable';
|
|||
import Tag from '@/ds-components/Tag';
|
||||
import { type RequestError } from '@/hooks/use-api';
|
||||
|
||||
import ApplicationScopesAssignmentModal from './ApplicationScopesAssignmentModal';
|
||||
import * as styles from './index.module.scss';
|
||||
import usePermissionsTable from './use-permissions-table';
|
||||
import useScopesTable from './use-scopes-table';
|
||||
|
||||
type Props = {
|
||||
application: Application;
|
||||
|
@ -21,71 +22,85 @@ function Permissions({ application }: Props) {
|
|||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const [isAssignScopesModalOpen, setIsAssignScopesModalOpen] = useState(false);
|
||||
const { parseRowGroup, deletePermission } = usePermissionsTable();
|
||||
const { parseRowGroup, deleteScope } = useScopesTable();
|
||||
|
||||
const { data, mutate, isLoading } = useSWR<ApplicationUserConsentScopesResponse, RequestError>(
|
||||
`api/applications/${application.id}/user-consent-scopes`
|
||||
);
|
||||
const { data, error, mutate, isLoading } = useSWR<
|
||||
ApplicationUserConsentScopesResponse,
|
||||
RequestError
|
||||
>(`api/applications/${application.id}/user-consent-scopes`);
|
||||
|
||||
const rowGroups = useMemo(() => parseRowGroup(data), [data, parseRowGroup]);
|
||||
|
||||
return (
|
||||
<FormCard
|
||||
title="application_details.permissions.name"
|
||||
description="application_details.permissions.description"
|
||||
>
|
||||
<TemplateTable
|
||||
className={styles.permissionsModal}
|
||||
name="application_details.permissions.table_name"
|
||||
rowIndexKey="id"
|
||||
isLoading={isLoading}
|
||||
rowGroups={rowGroups}
|
||||
columns={[
|
||||
{
|
||||
title: t('application_details.permissions.field_name'),
|
||||
dataIndex: 'name',
|
||||
colSpan: 5,
|
||||
render: ({ name }) => (
|
||||
<Tag variant="cell">
|
||||
<Breakable>{name}</Breakable>
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: `${t('general.description')} (${t(
|
||||
'application_details.permissions.field_description'
|
||||
)})`,
|
||||
dataIndex: 'description',
|
||||
colSpan: 5,
|
||||
render: ({ description }) => <Breakable>{description ?? '-'}</Breakable>,
|
||||
},
|
||||
{
|
||||
title: null,
|
||||
dataIndex: 'delete',
|
||||
render: (data) => (
|
||||
<ActionsButton
|
||||
fieldName="application_details.permissions.name"
|
||||
deleteConfirmation="application_details.permissions.permission_delete_confirm"
|
||||
textOverrides={{
|
||||
delete: 'application_details.permissions.delete_text',
|
||||
deleteConfirmation: 'general.remove',
|
||||
}}
|
||||
onEdit={() => {
|
||||
// TODO: Implement edit permission
|
||||
}}
|
||||
onDelete={async () => {
|
||||
await deletePermission(data, application.id);
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
onAdd={() => {
|
||||
setIsAssignScopesModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
</FormCard>
|
||||
<>
|
||||
<FormCard
|
||||
title="application_details.permissions.name"
|
||||
description="application_details.permissions.description"
|
||||
>
|
||||
<TemplateTable
|
||||
className={styles.permissionsModal}
|
||||
name="application_details.permissions.table_name"
|
||||
rowIndexKey="id"
|
||||
errorMessage={error?.body?.message ?? error?.message}
|
||||
isLoading={isLoading}
|
||||
rowGroups={rowGroups}
|
||||
columns={[
|
||||
{
|
||||
title: t('application_details.permissions.field_name'),
|
||||
dataIndex: 'name',
|
||||
colSpan: 5,
|
||||
render: ({ name }) => (
|
||||
<Tag variant="cell">
|
||||
<Breakable>{name}</Breakable>
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: `${t('general.description')} (${t(
|
||||
'application_details.permissions.field_description'
|
||||
)})`,
|
||||
dataIndex: 'description',
|
||||
colSpan: 5,
|
||||
render: ({ description }) => <Breakable>{description ?? '-'}</Breakable>,
|
||||
},
|
||||
{
|
||||
title: null,
|
||||
dataIndex: 'delete',
|
||||
render: (data) => (
|
||||
<ActionsButton
|
||||
fieldName="application_details.permissions.name"
|
||||
deleteConfirmation="application_details.permissions.permission_delete_confirm"
|
||||
textOverrides={{
|
||||
delete: 'application_details.permissions.delete_text',
|
||||
deleteConfirmation: 'general.remove',
|
||||
}}
|
||||
onEdit={() => {
|
||||
// TODO: Implement edit permission
|
||||
}}
|
||||
onDelete={async () => {
|
||||
await deleteScope(data, application.id);
|
||||
void mutate();
|
||||
}}
|
||||
/>
|
||||
),
|
||||
},
|
||||
]}
|
||||
onAdd={() => {
|
||||
setIsAssignScopesModalOpen(true);
|
||||
}}
|
||||
/>
|
||||
</FormCard>
|
||||
{/* Render the permissions assignment modal only if the data is fetched properly */}
|
||||
{data && (
|
||||
<ApplicationScopesAssignmentModal
|
||||
applicationId={application.id}
|
||||
isOpen={isAssignScopesModalOpen}
|
||||
onClose={() => {
|
||||
setIsAssignScopesModalOpen(false);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -9,36 +9,36 @@ import useApi from '@/hooks/use-api';
|
|||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type PermissionsTableRowDataType = {
|
||||
type ScopesTableRowDataType = {
|
||||
type: ApplicationUserConsentScopeType;
|
||||
id: string;
|
||||
name: string;
|
||||
description?: string;
|
||||
};
|
||||
|
||||
type PermissionsTableFieldGroupType = {
|
||||
type ScopesTableRowGroupType = {
|
||||
key: string;
|
||||
label: string;
|
||||
labelRowClassName?: string;
|
||||
data: PermissionsTableRowDataType[];
|
||||
data: ScopesTableRowDataType[];
|
||||
};
|
||||
|
||||
/**
|
||||
* - parseRowGroup: parse the application user consent scopes response data to table field group data
|
||||
*/
|
||||
const usePermissionsTable = () => {
|
||||
const useScopesTable = () => {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
const api = useApi();
|
||||
|
||||
const parseRowGroup = useCallback(
|
||||
(data?: ApplicationUserConsentScopesResponse): PermissionsTableFieldGroupType[] => {
|
||||
(data?: ApplicationUserConsentScopesResponse): ScopesTableRowGroupType[] => {
|
||||
if (!data) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const { organizationScopes, userScopes, resourceScopes } = data;
|
||||
|
||||
const userScopesGroup: PermissionsTableFieldGroupType = {
|
||||
const userScopesGroup: ScopesTableRowGroupType = {
|
||||
key: ApplicationUserConsentScopeType.UserScopes,
|
||||
label: t('application_details.permissions.user_permissions'),
|
||||
labelRowClassName: styles.sectionTitleRow,
|
||||
|
@ -50,7 +50,7 @@ const usePermissionsTable = () => {
|
|||
})),
|
||||
};
|
||||
|
||||
const organizationScopesGroup: PermissionsTableFieldGroupType = {
|
||||
const organizationScopesGroup: ScopesTableRowGroupType = {
|
||||
key: ApplicationUserConsentScopeType.OrganizationScopes,
|
||||
label: t('application_details.permissions.organization_permissions'),
|
||||
labelRowClassName: styles.sectionTitleRow,
|
||||
|
@ -62,7 +62,7 @@ const usePermissionsTable = () => {
|
|||
})),
|
||||
};
|
||||
|
||||
const resourceScopesGroups = resourceScopes.map<PermissionsTableFieldGroupType>(
|
||||
const resourceScopesGroups = resourceScopes.map<ScopesTableRowGroupType>(
|
||||
({ resource, scopes }) => ({
|
||||
key: resource.indicator,
|
||||
label: resource.name,
|
||||
|
@ -81,16 +81,16 @@ const usePermissionsTable = () => {
|
|||
[t]
|
||||
);
|
||||
|
||||
const deletePermission = useCallback(
|
||||
async (scope: PermissionsTableRowDataType, applicationId: string) =>
|
||||
const deleteScope = useCallback(
|
||||
async (scope: ScopesTableRowDataType, applicationId: string) =>
|
||||
api.delete(`api/applications/${applicationId}/user-consent-scopes/${scope.type}/${scope.id}`),
|
||||
[api]
|
||||
);
|
||||
|
||||
return {
|
||||
parseRowGroup,
|
||||
deletePermission,
|
||||
deleteScope,
|
||||
};
|
||||
};
|
||||
|
||||
export default usePermissionsTable;
|
||||
export default useScopesTable;
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Rolle',
|
||||
|
|
|
@ -109,6 +109,14 @@ const application_details = {
|
|||
delete_text: 'Remove permission',
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
user_profile: 'User profile',
|
||||
api_resource: 'API resource',
|
||||
organization: 'Organization',
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Role',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Rol',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Rôle',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Ruolo',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: '役割',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: '역할',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Role',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Função',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Nome da função',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Роль',
|
||||
|
|
|
@ -151,6 +151,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: 'Rol',
|
||||
|
|
|
@ -148,6 +148,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: '角色',
|
||||
|
|
|
@ -148,6 +148,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: '角色',
|
||||
|
|
|
@ -149,6 +149,21 @@ const application_details = {
|
|||
/** UNTRANSLATED */
|
||||
permission_delete_confirm:
|
||||
'This action will withdraw the permissions granted to the third-party app, preventing it from requesting user authorization for specific data types. Are you sure you want to continue?',
|
||||
/** UNTRANSLATED */
|
||||
permissions_assignment_description:
|
||||
'Select the permissions the third-party application requests for user authorization to access specific data types.',
|
||||
/** UNTRANSLATED */
|
||||
user_profile: 'User profile',
|
||||
/** UNTRANSLATED */
|
||||
api_resource: 'API resource',
|
||||
/** UNTRANSLATED */
|
||||
organization: 'Organization',
|
||||
/** UNTRANSLATED */
|
||||
user_permissions_assignment_form_title: 'Add the user profile permissions',
|
||||
/** UNTRANSLATED */
|
||||
organization_permissions_assignment_form_title: 'Add the organization permissions',
|
||||
/** UNTRANSLATED */
|
||||
api_resource_permissions_assignment_form_title: 'Add the API resource permissions',
|
||||
},
|
||||
roles: {
|
||||
name_column: '角色',
|
||||
|
|
Loading…
Add table
Reference in a new issue