mirror of
https://github.com/logto-io/logto.git
synced 2025-01-27 21:39:16 -05:00
refactor(console): setup m2m roles after creating m2m app (#5924)
This commit is contained in:
parent
558c1bccfb
commit
58fd32e456
28 changed files with 360 additions and 137 deletions
|
@ -15,12 +15,11 @@ import RadioGroup, { Radio } from '@/ds-components/RadioGroup';
|
|||
import TextInput from '@/ds-components/TextInput';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import useCurrentUser from '@/hooks/use-current-user';
|
||||
import TypeDescription from '@/pages/Applications/components/TypeDescription';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { applicationTypeI18nKey } from '@/types/applications';
|
||||
import { trySubmitSafe } from '@/utils/form';
|
||||
|
||||
import TypeDescription from '../TypeDescription';
|
||||
|
||||
import Footer from './Footer';
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
|
@ -31,7 +30,7 @@ type FormData = {
|
|||
isThirdParty?: boolean;
|
||||
};
|
||||
|
||||
type Props = {
|
||||
export type Props = {
|
||||
readonly isDefaultCreateThirdParty?: boolean;
|
||||
readonly defaultCreateType?: ApplicationType;
|
||||
readonly defaultCreateFrameworkName?: string;
|
|
@ -0,0 +1,57 @@
|
|||
import { ApplicationType, RoleType, type Application } from '@logto/schemas';
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import RoleAssignmentModal from '@/components/RoleAssignmentModal';
|
||||
import { isDevFeaturesEnabled } from '@/consts/env';
|
||||
|
||||
import CreateForm, { type Props as CreateApplicationFormProps } from './CreateForm';
|
||||
|
||||
type Props = Omit<CreateApplicationFormProps, 'onClose'> & {
|
||||
/**
|
||||
* The callback function that will be called when the application creation process is completed or canceled.
|
||||
*/
|
||||
readonly onCompleted?: (createdApp?: Application) => void;
|
||||
};
|
||||
|
||||
/**
|
||||
* The component for handling application creation (including create an application and setup its permissions if needed).
|
||||
*/
|
||||
function ApplicationCreation({ onCompleted, ...reset }: Props) {
|
||||
const [createdMachineToMachineApplication, setCreatedMachineToMachineApplication] =
|
||||
useState<Application>();
|
||||
|
||||
const createFormModalCloseHandler = useCallback(
|
||||
(createdApp?: Application) => {
|
||||
// Todo @xiaoyijun remove dev feature flag
|
||||
if (isDevFeaturesEnabled && createdApp?.type === ApplicationType.MachineToMachine) {
|
||||
setCreatedMachineToMachineApplication(createdApp);
|
||||
return;
|
||||
}
|
||||
|
||||
onCompleted?.(createdApp);
|
||||
},
|
||||
[onCompleted]
|
||||
);
|
||||
|
||||
if (createdMachineToMachineApplication) {
|
||||
return (
|
||||
<RoleAssignmentModal
|
||||
isSkippable
|
||||
isMachineToMachineRoleCreationHintVisible
|
||||
entity={createdMachineToMachineApplication}
|
||||
type={RoleType.MachineToMachine}
|
||||
modalTextOverrides={{
|
||||
title: 'applications.m2m_role_assignment.title',
|
||||
subtitle: 'applications.m2m_role_assignment.subtitle',
|
||||
}}
|
||||
onClose={() => {
|
||||
onCompleted?.(createdMachineToMachineApplication);
|
||||
}}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
return <CreateForm {...reset} onClose={createFormModalCloseHandler} />;
|
||||
}
|
||||
|
||||
export default ApplicationCreation;
|
|
@ -0,0 +1,6 @@
|
|||
@use '@/scss/underscore' as _;
|
||||
|
||||
.hint {
|
||||
margin-top: _.unit(2);
|
||||
font: var(--font-body-2);
|
||||
}
|
168
packages/console/src/components/RoleAssignmentModal/index.tsx
Normal file
168
packages/console/src/components/RoleAssignmentModal/index.tsx
Normal file
|
@ -0,0 +1,168 @@
|
|||
import { type AdminConsoleKey } from '@logto/phrases';
|
||||
import type { RoleResponse, UserProfileResponse, Application } from '@logto/schemas';
|
||||
import { RoleType } from '@logto/schemas';
|
||||
import { cond } from '@silverhand/essentials';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import RolesTransfer from '@/components/RolesTransfer';
|
||||
import Button from '@/ds-components/Button';
|
||||
import DangerousRaw from '@/ds-components/DangerousRaw';
|
||||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
import TextLink from '@/ds-components/TextLink';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { getUserTitle } from '@/utils/user';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
||||
type Props = (
|
||||
| {
|
||||
entity: UserProfileResponse;
|
||||
type: RoleType.User;
|
||||
}
|
||||
| {
|
||||
entity: Application;
|
||||
type: RoleType.MachineToMachine;
|
||||
}
|
||||
) & {
|
||||
readonly onClose: (success?: boolean) => void;
|
||||
/**
|
||||
* The overrides for the modal text.
|
||||
* If specified, the title will be overridden.
|
||||
* If not specified, the default title will vary based on the type.
|
||||
*/
|
||||
readonly modalTextOverrides?: {
|
||||
title?: AdminConsoleKey;
|
||||
subtitle?: AdminConsoleKey;
|
||||
};
|
||||
readonly isSkippable?: boolean;
|
||||
readonly isMachineToMachineRoleCreationHintVisible?: boolean;
|
||||
};
|
||||
|
||||
function RoleAssignmentModal({
|
||||
entity,
|
||||
onClose,
|
||||
type,
|
||||
modalTextOverrides,
|
||||
isSkippable,
|
||||
isMachineToMachineRoleCreationHintVisible,
|
||||
}: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [roles, setRoles] = useState<RoleResponse[]>([]);
|
||||
const isForUser = type === RoleType.User;
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const handleAssign = async () => {
|
||||
if (isSubmitting || roles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await api.post(`api/${isForUser ? 'users' : 'applications'}/${entity.id}/roles`, {
|
||||
json: { roleIds: roles.map(({ id }) => id) },
|
||||
});
|
||||
toast.success(t('user_details.roles.role_assigned'));
|
||||
onClose(true);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
isOpen
|
||||
shouldCloseOnEsc
|
||||
className={modalStyles.content}
|
||||
overlayClassName={modalStyles.overlay}
|
||||
onRequestClose={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<ModalLayout
|
||||
title={
|
||||
cond(modalTextOverrides?.title) ?? (
|
||||
<DangerousRaw>
|
||||
{t(
|
||||
isForUser
|
||||
? 'user_details.roles.assign_title'
|
||||
: 'application_details.roles.assign_title',
|
||||
{ name: isForUser ? getUserTitle(entity) : entity.name }
|
||||
)}
|
||||
</DangerousRaw>
|
||||
)
|
||||
}
|
||||
subtitle={
|
||||
cond(modalTextOverrides?.subtitle) ?? (
|
||||
<DangerousRaw>
|
||||
{t(
|
||||
isForUser
|
||||
? 'user_details.roles.assign_subtitle'
|
||||
: 'application_details.roles.assign_subtitle',
|
||||
{ name: isForUser ? getUserTitle(entity) : entity.name }
|
||||
)}
|
||||
</DangerousRaw>
|
||||
)
|
||||
}
|
||||
size="large"
|
||||
footer={
|
||||
<>
|
||||
{isSkippable && (
|
||||
<Button
|
||||
isLoading={isSubmitting}
|
||||
title="general.skip"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
onClose();
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
isLoading={isSubmitting}
|
||||
disabled={roles.length === 0}
|
||||
htmlType="submit"
|
||||
title={
|
||||
isForUser
|
||||
? 'user_details.roles.confirm_assign'
|
||||
: 'application_details.roles.confirm_assign'
|
||||
}
|
||||
size="large"
|
||||
type="primary"
|
||||
onClick={handleAssign}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
<RolesTransfer
|
||||
entityId={entity.id}
|
||||
type={type}
|
||||
value={roles}
|
||||
onChange={(value) => {
|
||||
setRoles(value);
|
||||
}}
|
||||
/>
|
||||
{!isForUser && isMachineToMachineRoleCreationHintVisible && (
|
||||
<div className={styles.hint}>
|
||||
<Trans
|
||||
components={{
|
||||
a: <TextLink to="/roles" />,
|
||||
}}
|
||||
>
|
||||
{t('applications.m2m_role_assignment.role_creation_hint')}
|
||||
</Trans>
|
||||
</div>
|
||||
)}
|
||||
</ModalLayout>
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default RoleAssignmentModal;
|
|
@ -12,6 +12,7 @@ import MachineToMachineRoleIcon from '@/assets/icons/m2m-role.svg';
|
|||
import Plus from '@/assets/icons/plus.svg';
|
||||
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import RoleAssignmentModal from '@/components/RoleAssignmentModal';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import Button from '@/ds-components/Button';
|
||||
import ConfirmModal from '@/ds-components/ConfirmModal';
|
||||
|
@ -23,7 +24,6 @@ import type { RequestError } from '@/hooks/use-api';
|
|||
import useApi from '@/hooks/use-api';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
import AssignToRoleModal from '@/pages/Roles/components/AssignToRoleModal';
|
||||
import { buildUrl, formatSearchKeyword } from '@/utils/url';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -176,7 +176,7 @@ function MachineToMachineApplicationRoles({ application }: Props) {
|
|||
</ConfirmModal>
|
||||
)}
|
||||
{isAssignRolesModalOpen && (
|
||||
<AssignToRoleModal
|
||||
<RoleAssignmentModal
|
||||
entity={application}
|
||||
type={RoleType.MachineToMachine}
|
||||
onClose={(success) => {
|
||||
|
|
|
@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next';
|
|||
import { useLocation } from 'react-router-dom';
|
||||
|
||||
import SearchIcon from '@/assets/icons/search.svg';
|
||||
import ApplicationCreation from '@/components/ApplicationCreation';
|
||||
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
|
||||
import FeatureTag from '@/components/FeatureTag';
|
||||
import { type SelectedGuide } from '@/components/Guide/GuideCard';
|
||||
|
@ -21,7 +22,6 @@ import useTenantPathname from '@/hooks/use-tenant-pathname';
|
|||
import { allAppGuideCategories, type AppGuideCategory } from '@/types/applications';
|
||||
import { thirdPartyAppCategory } from '@/types/applications';
|
||||
|
||||
import CreateForm from '../CreateForm';
|
||||
import ProtectedAppCard from '../ProtectedAppCard';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -70,7 +70,7 @@ function GuideLibrary({ className, hasCardBorder, hasCardButton }: Props) {
|
|||
setSelectedGuide(data);
|
||||
}, []);
|
||||
|
||||
const onCloseCreateForm = useCallback(
|
||||
const onAppCreationCompleted = useCallback(
|
||||
(newApp?: Application) => {
|
||||
if (newApp && selectedGuide) {
|
||||
navigate(
|
||||
|
@ -185,11 +185,11 @@ function GuideLibrary({ className, hasCardBorder, hasCardButton }: Props) {
|
|||
</div>
|
||||
</div>
|
||||
{selectedGuide?.target !== 'API' && showCreateForm && (
|
||||
<CreateForm
|
||||
<ApplicationCreation
|
||||
defaultCreateType={selectedGuide?.target}
|
||||
defaultCreateFrameworkName={selectedGuide?.name}
|
||||
isDefaultCreateThirdParty={selectedGuide?.isThirdParty}
|
||||
onClose={onCloseCreateForm}
|
||||
onCompleted={onAppCreationCompleted}
|
||||
/>
|
||||
)}
|
||||
</OverlayScrollbar>
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
import { useState } from 'react';
|
||||
import Modal from 'react-modal';
|
||||
|
||||
import ApplicationCreation from '@/components/ApplicationCreation';
|
||||
import ModalFooter from '@/components/Guide/ModalFooter';
|
||||
import ModalHeader from '@/components/Guide/ModalHeader';
|
||||
import useTenantPathname from '@/hooks/use-tenant-pathname';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
|
||||
import CreateForm from '../CreateForm';
|
||||
import GuideLibrary from '../GuideLibrary';
|
||||
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -47,8 +47,8 @@ function GuideLibraryModal({ isOpen, onClose }: Props) {
|
|||
/>
|
||||
</div>
|
||||
{showCreateForm && (
|
||||
<CreateForm
|
||||
onClose={(newApp) => {
|
||||
<ApplicationCreation
|
||||
onCompleted={(newApp) => {
|
||||
if (newApp) {
|
||||
navigate(`/applications/${newApp.id}`);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import CreateRoleDark from '@/assets/icons/create-role-dark.svg';
|
|||
import CreateRole from '@/assets/icons/create-role.svg';
|
||||
import SocialDark from '@/assets/icons/social-dark.svg';
|
||||
import Social from '@/assets/icons/social.svg';
|
||||
import ApplicationCreation from '@/components/ApplicationCreation';
|
||||
import { type SelectedGuide } from '@/components/Guide/GuideCard';
|
||||
import GuideCardGroup from '@/components/Guide/GuideCardGroup';
|
||||
import { useApiGuideMetadata, useAppGuideMetadata } from '@/components/Guide/hooks';
|
||||
|
@ -25,7 +26,6 @@ import useTheme from '@/hooks/use-theme';
|
|||
import useWindowResize from '@/hooks/use-window-resize';
|
||||
|
||||
import CreateApiForm from '../ApiResources/components/CreateForm';
|
||||
import CreateAppForm from '../Applications/components/CreateForm';
|
||||
|
||||
import ProtectedAppCreationForm from './ProtectedAppCreationForm';
|
||||
import * as styles from './index.module.scss';
|
||||
|
@ -71,7 +71,7 @@ function GetStarted() {
|
|||
setSelectedGuide(data);
|
||||
}, []);
|
||||
|
||||
const onCloseCreateAppForm = useCallback(
|
||||
const onAppCreationCompleted = useCallback(
|
||||
(newApp?: Application) => {
|
||||
if (newApp && selectedGuide) {
|
||||
navigate(`/applications/${newApp.id}/guide/${selectedGuide.id}`, { replace: true });
|
||||
|
@ -125,10 +125,10 @@ function GetStarted() {
|
|||
onClickGuide={onClickAppGuide}
|
||||
/>
|
||||
{selectedGuide?.target !== 'API' && showCreateAppForm && (
|
||||
<CreateAppForm
|
||||
<ApplicationCreation
|
||||
defaultCreateType={selectedGuide?.target}
|
||||
defaultCreateFrameworkName={selectedGuide?.name}
|
||||
onClose={onCloseCreateAppForm}
|
||||
onCompleted={onAppCreationCompleted}
|
||||
/>
|
||||
)}
|
||||
<TextLink to="/applications/create">{t('get_started.view_all')}</TextLink>
|
||||
|
|
|
@ -1,119 +0,0 @@
|
|||
import type { RoleResponse, UserProfileResponse, Application } from '@logto/schemas';
|
||||
import { RoleType } from '@logto/schemas';
|
||||
import { useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import ReactModal from 'react-modal';
|
||||
|
||||
import RolesTransfer from '@/components/RolesTransfer';
|
||||
import Button from '@/ds-components/Button';
|
||||
import DangerousRaw from '@/ds-components/DangerousRaw';
|
||||
import ModalLayout from '@/ds-components/ModalLayout';
|
||||
import useApi from '@/hooks/use-api';
|
||||
import * as modalStyles from '@/scss/modal.module.scss';
|
||||
import { getUserTitle } from '@/utils/user';
|
||||
|
||||
type Props =
|
||||
| {
|
||||
entity: UserProfileResponse;
|
||||
onClose: (success?: boolean) => void;
|
||||
type: RoleType.User;
|
||||
}
|
||||
| {
|
||||
entity: Application;
|
||||
onClose: (success?: boolean) => void;
|
||||
type: RoleType.MachineToMachine;
|
||||
};
|
||||
|
||||
function AssignToRoleModal({ entity, onClose, type }: Props) {
|
||||
const { t } = useTranslation(undefined, { keyPrefix: 'admin_console' });
|
||||
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [roles, setRoles] = useState<RoleResponse[]>([]);
|
||||
|
||||
const api = useApi();
|
||||
|
||||
const handleAssign = async () => {
|
||||
if (isSubmitting || roles.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
setIsSubmitting(true);
|
||||
|
||||
try {
|
||||
await api.post(
|
||||
`api/${type === RoleType.User ? 'users' : 'applications'}/${entity.id}/roles`,
|
||||
{
|
||||
json: { roleIds: roles.map(({ id }) => id) },
|
||||
}
|
||||
);
|
||||
toast.success(t('user_details.roles.role_assigned'));
|
||||
onClose(true);
|
||||
} finally {
|
||||
setIsSubmitting(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ReactModal
|
||||
isOpen
|
||||
shouldCloseOnEsc
|
||||
className={modalStyles.content}
|
||||
overlayClassName={modalStyles.overlay}
|
||||
onRequestClose={() => {
|
||||
onClose();
|
||||
}}
|
||||
>
|
||||
<ModalLayout
|
||||
title={
|
||||
<DangerousRaw>
|
||||
{t(
|
||||
type === RoleType.User
|
||||
? 'user_details.roles.assign_title'
|
||||
: 'application_details.roles.assign_title',
|
||||
{ name: type === RoleType.User ? getUserTitle(entity) : entity.name }
|
||||
)}
|
||||
</DangerousRaw>
|
||||
}
|
||||
subtitle={
|
||||
<DangerousRaw>
|
||||
{t(
|
||||
type === RoleType.User
|
||||
? 'user_details.roles.assign_subtitle'
|
||||
: 'application_details.roles.assign_subtitle',
|
||||
{ name: type === RoleType.User ? getUserTitle(entity) : entity.name }
|
||||
)}
|
||||
</DangerousRaw>
|
||||
}
|
||||
size="large"
|
||||
footer={
|
||||
<Button
|
||||
isLoading={isSubmitting}
|
||||
disabled={roles.length === 0}
|
||||
htmlType="submit"
|
||||
title={
|
||||
type === RoleType.User
|
||||
? 'user_details.roles.confirm_assign'
|
||||
: 'application_details.roles.confirm_assign'
|
||||
}
|
||||
size="large"
|
||||
type="primary"
|
||||
onClick={handleAssign}
|
||||
/>
|
||||
}
|
||||
onClose={onClose}
|
||||
>
|
||||
<RolesTransfer
|
||||
entityId={entity.id}
|
||||
type={type}
|
||||
value={roles}
|
||||
onChange={(value) => {
|
||||
setRoles(value);
|
||||
}}
|
||||
/>
|
||||
</ModalLayout>
|
||||
</ReactModal>
|
||||
);
|
||||
}
|
||||
|
||||
export default AssignToRoleModal;
|
|
@ -13,6 +13,7 @@ import UserRoleIconDark from '@/assets/icons/user-role-dark.svg';
|
|||
import UserRoleIcon from '@/assets/icons/user-role.svg';
|
||||
import EmptyDataPlaceholder from '@/components/EmptyDataPlaceholder';
|
||||
import ItemPreview from '@/components/ItemPreview';
|
||||
import RoleAssignmentModal from '@/components/RoleAssignmentModal';
|
||||
import { defaultPageSize } from '@/consts';
|
||||
import Button from '@/ds-components/Button';
|
||||
import ConfirmModal from '@/ds-components/ConfirmModal';
|
||||
|
@ -24,7 +25,6 @@ import type { RequestError } from '@/hooks/use-api';
|
|||
import useApi from '@/hooks/use-api';
|
||||
import useSearchParametersWatcher from '@/hooks/use-search-parameters-watcher';
|
||||
import useTheme from '@/hooks/use-theme';
|
||||
import AssignToRoleModal from '@/pages/Roles/components/AssignToRoleModal';
|
||||
import { buildUrl, formatSearchKeyword } from '@/utils/url';
|
||||
|
||||
import type { UserDetailsOutletContext } from '../types';
|
||||
|
@ -172,7 +172,7 @@ function UserRoles() {
|
|||
</ConfirmModal>
|
||||
)}
|
||||
{isAssignRolesModalOpen && (
|
||||
<AssignToRoleModal
|
||||
<RoleAssignmentModal
|
||||
entity={user}
|
||||
type={RoleType.User}
|
||||
onClose={(success) => {
|
||||
|
|
|
@ -252,6 +252,27 @@ describe('applications', () => {
|
|||
|
||||
await waitForToast(page, { text: 'Application created successfully.' });
|
||||
|
||||
// Expect to assign management API access role for the M2M app
|
||||
if (app.type === ApplicationType.MachineToMachine) {
|
||||
await expectModalWithTitle(
|
||||
page,
|
||||
'Authorize app with machine-to-machine role for permissions'
|
||||
);
|
||||
|
||||
await expect(page).toClick(
|
||||
'.ReactModalPortal div[class$=rolesTransfer] div[class$=item] div',
|
||||
{
|
||||
text: 'Logto Management API access',
|
||||
}
|
||||
);
|
||||
|
||||
await expectToClickModalAction(page, 'Assign roles');
|
||||
|
||||
await waitForToast(page, {
|
||||
text: 'Successfully assigned role(s)',
|
||||
});
|
||||
}
|
||||
|
||||
await expect(page).toMatchElement('div[class$=main] div[class$=header] div[class$=name]', {
|
||||
text: app.name,
|
||||
});
|
||||
|
|
|
@ -55,6 +55,13 @@ const applications = {
|
|||
placeholder_title: 'Wähle einen Anwendungstyp, um fortzufahren',
|
||||
placeholder_description:
|
||||
'Logto verwendet eine Anwendungs-Entität für OIDC, um Aufgaben wie die Identifizierung deiner Apps, das Management der Anmeldung und die Erstellung von Prüfprotokollen zu erleichtern.',
|
||||
m2m_role_assignment: {
|
||||
title: 'App autorisieren mit maschinenbasierten Rollen für Berechtigungen',
|
||||
subtitle:
|
||||
'Maschine-zu-Maschine-Anwendungen erfordern eine autorisierte Maschine-zu-Maschine-Rolle.',
|
||||
role_creation_hint:
|
||||
'Hat keine Maschine-zu-Maschine-Rolle? <a>Erstellen Sie zuerst eine Maschine-zu-Maschine</a>-Rolle',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -53,6 +53,12 @@ const applications = {
|
|||
placeholder_title: 'Select an application type to continue',
|
||||
placeholder_description:
|
||||
'Logto uses an application entity for OIDC to help with tasks such as identifying your apps, managing sign-in, and creating audit logs.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Authorize app with machine-to-machine role for permissions',
|
||||
subtitle: 'Machine-to-machine applications require authorized machine-to-machine role.',
|
||||
role_creation_hint:
|
||||
'Doesn’t have a machine-to-machine role? <a>Create a machine-to-machine</a> role first',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,13 @@ const applications = {
|
|||
placeholder_title: 'Selecciona un tipo de aplicación para continuar',
|
||||
placeholder_description:
|
||||
'Logto utiliza una entidad de aplicación para OIDC para ayudar con tareas como la identificación de tus aplicaciones, la gestión de inicio de sesión y la creación de registros de auditoría.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Autorizar la aplicación con rol de máquina a máquina para permisos',
|
||||
subtitle:
|
||||
'Las aplicaciones de máquina a máquina requieren un rol de máquina a máquina autorizado.',
|
||||
role_creation_hint:
|
||||
'¿No tiene un rol de máquina a máquina? <a>Cree primero un rol de máquina a máquina</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -56,6 +56,12 @@ const applications = {
|
|||
placeholder_title: "Sélectionnez un type d'application pour continuer",
|
||||
placeholder_description:
|
||||
"Logto utilise une entité d'application pour OIDC pour aider aux tâches telles que l'identification de vos applications, la gestion de la connexion et la création de journaux d'audit",
|
||||
m2m_role_assignment: {
|
||||
title: "Autoriser l'application avec un rôle machine à machine pour les permissions",
|
||||
subtitle: 'Les applications machine à machine nécessitent un rôle machine à machine autorisé.',
|
||||
role_creation_hint:
|
||||
'N’a pas de rôle machine à machine ? <a>Créez d’abord un rôle machine à machine</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,13 @@ const applications = {
|
|||
placeholder_title: 'Seleziona un tipo di applicazione per continuare',
|
||||
placeholder_description:
|
||||
"Logto utilizza un'entità applicazione per OIDC per aiutarti in compiti come l'identificazione delle tue app, la gestione dell'accesso e la creazione di registri di audit.",
|
||||
m2m_role_assignment: {
|
||||
title: "Autorizza l'applicazione con ruolo da macchina a macchina per le autorizzazioni",
|
||||
subtitle:
|
||||
'Le applicazioni da macchina a macchina richiedono un ruolo da macchina a macchina autorizzato.',
|
||||
role_creation_hint:
|
||||
'Non ha un ruolo da macchina a macchina? <a>Crea prima un ruolo da macchina a macchina</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -54,6 +54,12 @@ const applications = {
|
|||
placeholder_title: '続行するにはアプリケーションタイプを選択してください',
|
||||
placeholder_description:
|
||||
'LogtoはOIDCのためにアプリケーションエンティティを使用して、アプリケーションの識別、サインインの管理、監査ログの作成などのタスクをサポートします。',
|
||||
m2m_role_assignment: {
|
||||
title: 'アプリを権限付きのマシン間ロールで認可する',
|
||||
subtitle: 'マシン間アプリケーションには承認されたマシン間ロールが必要です。',
|
||||
role_creation_hint:
|
||||
'マシン間ロールがありませんか? <a>まずマシン間</a> ロールを作成してください',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -54,6 +54,11 @@ const applications = {
|
|||
placeholder_title: '어플리케이션 유형을 선택하여 계속하세요',
|
||||
placeholder_description:
|
||||
'Logto는 OIDC용 앱 엔티티를 사용하여 앱 식별, 로그인 관리 및 감사 로그 생성과 같은 작업을 지원합니다.',
|
||||
m2m_role_assignment: {
|
||||
title: '권한을 위해 기계 간 역할로 앱을 인증합니다',
|
||||
subtitle: '기계 간 애플리케이션은 인증된 기계 간 역할이 필요합니다.',
|
||||
role_creation_hint: '기계 간 역할이 없습니까? <a>먼저 기계 간</a> 역할을 만드십시오',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,12 @@ const applications = {
|
|||
placeholder_title: 'Wybierz typ aplikacji, aby kontynuować',
|
||||
placeholder_description:
|
||||
'Logto używa jednostki aplikacji dla OIDC, aby pomóc w takich zadaniach jak identyfikowanie Twoich aplikacji, zarządzanie logowaniem i tworzenie dzienników audytu.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Autoryzuj aplikację z rolą maszyny do maszyny dla uprawnień',
|
||||
subtitle: 'Aplikacje maszyna-do-maszyna wymagają autoryzowanej roli maszyna-do-maszyna.',
|
||||
role_creation_hint:
|
||||
'Nie ma roli maszyna-do-maszyna? <a>Najpierw utwórz rolę maszyna-do-maszyna</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,13 @@ const applications = {
|
|||
placeholder_title: 'Selecione um tipo de aplicativo para continuar',
|
||||
placeholder_description:
|
||||
'O Logto usa uma entidade de aplicativo para OIDC para ajudar nas tarefas, como identificar seus aplicativos, gerenciar o login e criar logs de auditoria.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Autorizar aplicativo com função de máquina para máquina para permissões',
|
||||
subtitle:
|
||||
'Aplicativos de máquina para máquina requerem uma função de máquina para máquina autorizada.',
|
||||
role_creation_hint:
|
||||
'Não tem uma função de máquina para máquina? <a>Crie primeiro uma função de máquina para máquina</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -54,6 +54,13 @@ const applications = {
|
|||
placeholder_title: 'Selecione um tipo de aplicação para continuar',
|
||||
placeholder_description:
|
||||
'O Logto usa uma entidade de aplicativo para OIDC para ajudar em tarefas como identificar seus aplicativos, gerenciar o registro e criar registros de auditoria.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Autorizar aplicação com papel de máquina para máquina para permissões',
|
||||
subtitle:
|
||||
'Aplicações de máquina para máquina requerem uma função de máquina para máquina autorizada.',
|
||||
role_creation_hint:
|
||||
'Não tem uma função de máquina para máquina? <a>Crie primeiro uma função de máquina para máquina</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,12 @@ const applications = {
|
|||
placeholder_title: 'Выберите тип приложения, чтобы продолжить',
|
||||
placeholder_description:
|
||||
'Logto использует сущность приложения для OIDC для выполнения задач, таких как идентификация ваших приложений, управление входом в систему и создание журналов аудита.',
|
||||
m2m_role_assignment: {
|
||||
title: 'Авторизовать приложение с ролью от машины к машине для разрешений',
|
||||
subtitle: 'Приложения от машины к машине требуют авторизованной роли от машины к машине.',
|
||||
role_creation_hint:
|
||||
'У вас нет роли от машины к машине? <a>Сначала создайте роль от машины к машине</a>',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -55,6 +55,12 @@ const applications = {
|
|||
placeholder_title: 'Devam etmek için bir uygulama tipi seçin',
|
||||
placeholder_description:
|
||||
'Logto, uygulamanızı tanımlamaya, oturum açmayı yönetmeye ve denetim kayıtları oluşturmaya yardımcı olmak için OIDC için bir uygulama varlığı kullanır.',
|
||||
m2m_role_assignment: {
|
||||
title: 'İzinler için makineye özel rolle uygulamayı yetkilendir',
|
||||
subtitle: 'Makine-makine uygulamaları yetkilendirilmiş bir makine-makine rolü gerektirir.',
|
||||
role_creation_hint:
|
||||
'Makine-makine rolünüz yok mu? <a>Önce bir makine-makine</a> rolü oluşturun',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -52,6 +52,11 @@ const applications = {
|
|||
placeholder_title: '选择应用程序类型以继续',
|
||||
placeholder_description:
|
||||
'Logto 使用 OIDC 的应用程序实体来帮助识别你的应用程序、管理登录和创建审计日志等任务。',
|
||||
m2m_role_assignment: {
|
||||
title: '使用机器到机器角色授权应用程序以获取权限',
|
||||
subtitle: '机器对机器应用程序需要经过授权的机器对机器角色。',
|
||||
role_creation_hint: '没有机器对机器角色?先 <a>创建机器对机器</a> 角色',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -52,6 +52,11 @@ const applications = {
|
|||
placeholder_title: '選擇應用程式類型以繼續',
|
||||
placeholder_description:
|
||||
'Logto 使用 OIDC 的應用程式實體來幫助識別您的應用程式、管理登錄和創建審核日誌等任務。',
|
||||
m2m_role_assignment: {
|
||||
title: '使用機器到機器角色為應用程式授權權限',
|
||||
subtitle: '機器對機器應用程式需要經過授權的機器對機器角色。',
|
||||
role_creation_hint: '沒有機器對機器角色?先 <a>創建機器對機器</a> 角色',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
|
@ -52,6 +52,11 @@ const applications = {
|
|||
placeholder_title: '選擇應用程式類型以繼續',
|
||||
placeholder_description:
|
||||
'Logto 使用 OIDC 的應用程式實體來幫助識別你的應用程式、管理登入和創建審計日誌等任務。',
|
||||
m2m_role_assignment: {
|
||||
title: '使用機器到機器角色為應用程式授權權限',
|
||||
subtitle: '機器對機器應用程式需要經過授權的機器對機器角色。',
|
||||
role_creation_hint: '沒有機器對機器角色? 先 <a>創建機器對機器</a> 角色',
|
||||
},
|
||||
};
|
||||
|
||||
export default Object.freeze(applications);
|
||||
|
|
Loading…
Add table
Reference in a new issue