diff --git a/packages/console/src/pages/OrganizationRoleDetails/index.tsx b/packages/console/src/pages/OrganizationRoleDetails/index.tsx
index 835119814..4d6f404f2 100644
--- a/packages/console/src/pages/OrganizationRoleDetails/index.tsx
+++ b/packages/console/src/pages/OrganizationRoleDetails/index.tsx
@@ -1,4 +1,4 @@
-import { type OrganizationRole } from '@logto/schemas';
+import { roleTypeToKey, type OrganizationRole } from '@logto/schemas';
import classNames from 'classnames';
import { useEffect, useState } from 'react';
import { toast } from 'react-hot-toast';
@@ -81,7 +81,7 @@ function OrganizationRoleDetails() {
}
title={data.name}
- primaryTag={t('organization_role_details.org_role')}
+ primaryTag={t(`roles.type_${roleTypeToKey[data.type]}`)}
identifier={{ name: 'ID', value: data.id }}
actionMenuItems={[
{
diff --git a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/CreateOrganizationRoleModal.tsx b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/CreateOrganizationRoleModal.tsx
index 1338228e7..fba7aec2e 100644
--- a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/CreateOrganizationRoleModal.tsx
+++ b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/CreateOrganizationRoleModal.tsx
@@ -1,19 +1,32 @@
-import { type OrganizationRole } from '@logto/schemas';
+import { type AdminConsoleKey } from '@logto/phrases';
+import { RoleType, type OrganizationRole } from '@logto/schemas';
import { useCallback } from 'react';
-import { useForm } from 'react-hook-form';
+import { Controller, useForm } from 'react-hook-form';
import { toast } from 'react-hot-toast';
import { useTranslation } from 'react-i18next';
import ReactModal from 'react-modal';
+import { isDevFeaturesEnabled } from '@/consts/env';
import Button from '@/ds-components/Button';
+import DynamicT from '@/ds-components/DynamicT';
import FormField from '@/ds-components/FormField';
import ModalLayout from '@/ds-components/ModalLayout';
+import RadioGroup, { Radio } from '@/ds-components/RadioGroup';
import TextInput from '@/ds-components/TextInput';
import useApi from '@/hooks/use-api';
import * as modalStyles from '@/scss/modal.module.scss';
import { trySubmitSafe } from '@/utils/form';
-type FormData = Pick;
+import * as styles from './index.module.scss';
+
+type FormData = Pick;
+
+type RadioOption = { key: AdminConsoleKey; value: RoleType };
+
+const radioOptions: RadioOption[] = [
+ { key: 'roles.type_user', value: RoleType.User },
+ { key: 'roles.type_machine_to_machine', value: RoleType.MachineToMachine },
+];
type Props = {
readonly isOpen: boolean;
@@ -25,10 +38,11 @@ function CreateOrganizationRoleModal({ isOpen, onClose }: Props) {
const {
register,
+ control,
formState: { errors, isSubmitting },
handleSubmit,
reset,
- } = useForm();
+ } = useForm({ defaultValues: { type: RoleType.User } });
const onCloseHandler = useCallback(
(createdData?: OrganizationRole) => {
@@ -72,9 +86,10 @@ function CreateOrganizationRoleModal({ isOpen, onClose }: Props) {
onClick={submit}
/>
}
+ className={styles.createModal}
onClose={onCloseHandler}
>
-
+
-
+
+ {/* TODO: Remove */}
+ {isDevFeaturesEnabled && (
+
+ (
+ {
+ onChange(value);
+ }}
+ >
+ {radioOptions.map(({ key, value }) => (
+ } value={value} />
+ ))}
+
+ )}
+ />
+
+ )}
);
diff --git a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.module.scss b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.module.scss
index da411ee4a..dbd538746 100644
--- a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.module.scss
+++ b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.module.scss
@@ -1,5 +1,11 @@
@use '@/scss/underscore' as _;
+.createModal {
+ .roleTypes {
+ margin-top: _.unit(2);
+ }
+}
+
.permissions {
display: flex;
flex-wrap: wrap;
diff --git a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx
index 5c39edf8c..b0a5d3fdc 100644
--- a/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx
+++ b/packages/console/src/pages/OrganizationTemplate/OrganizationRoles/index.tsx
@@ -1,4 +1,4 @@
-import { type OrganizationRoleWithScopes } from '@logto/schemas';
+import { roleTypeToKey, type OrganizationRoleWithScopes } from '@logto/schemas';
import { cond } from '@silverhand/essentials';
import { useState } from 'react';
import { useTranslation } from 'react-i18next';
@@ -88,6 +88,14 @@ function OrganizationRoles() {
);
},
},
+ {
+ title: ,
+ dataIndex: 'type',
+ colSpan: 4,
+ render: ({ type }) => {
+ return ;
+ },
+ },
]}
rowClickHandler={({ id }) => {
navigate(id);
diff --git a/packages/console/src/pages/Roles/index.tsx b/packages/console/src/pages/Roles/index.tsx
index d7b693430..0a7f7daea 100644
--- a/packages/console/src/pages/Roles/index.tsx
+++ b/packages/console/src/pages/Roles/index.tsx
@@ -1,4 +1,4 @@
-import { RoleType, type RoleResponse } from '@logto/schemas';
+import { RoleType, roleTypeToKey, type RoleResponse } from '@logto/schemas';
import { Theme } from '@logto/schemas';
import { conditional } from '@silverhand/essentials';
import { useTranslation } from 'react-i18next';
@@ -108,11 +108,7 @@ function Roles() {
title: t('roles.col_type'),
dataIndex: 'type',
colSpan: 4,
- render: ({ type }) => (
-
- {type === RoleType.User ? t('roles.type_user') : t('roles.type_machine_to_machine')}
-
- ),
+ render: ({ type }) => {t(`roles.type_${roleTypeToKey[type]}`)},
},
{
title: t('roles.col_description'),
diff --git a/packages/integration-tests/src/api/organization-role.ts b/packages/integration-tests/src/api/organization-role.ts
index eabe04ae4..57827f67a 100644
--- a/packages/integration-tests/src/api/organization-role.ts
+++ b/packages/integration-tests/src/api/organization-role.ts
@@ -3,6 +3,7 @@ import {
type OrganizationRole,
type OrganizationRoleWithScopes,
type Scope,
+ type RoleType,
} from '@logto/schemas';
import { authedAdminApi } from './api.js';
@@ -11,6 +12,7 @@ import { ApiFactory } from './factory.js';
export type CreateOrganizationRolePostData = {
name: string;
description?: string;
+ type?: RoleType;
organizationScopeIds?: string[];
resourceScopeIds?: string[];
};
diff --git a/packages/integration-tests/src/tests/api/organization/organization-application.test.ts b/packages/integration-tests/src/tests/api/organization/organization-application.test.ts
index ae1f949a4..9d3514059 100644
--- a/packages/integration-tests/src/tests/api/organization/organization-application.test.ts
+++ b/packages/integration-tests/src/tests/api/organization/organization-application.test.ts
@@ -4,6 +4,7 @@ import {
ApplicationType,
type ApplicationWithOrganizationRoles,
type Application,
+ RoleType,
} from '@logto/schemas';
import { HTTPError } from 'ky';
@@ -70,8 +71,14 @@ devFeatureTest.describe('organization application APIs', () => {
const organizationId = organizationApi.organizations[0]!.id;
const app = applications[0]!;
const roles = await Promise.all([
- organizationApi.roleApi.create({ name: generateTestName() }),
- organizationApi.roleApi.create({ name: generateTestName() }),
+ organizationApi.roleApi.create({
+ name: generateTestName(),
+ type: RoleType.MachineToMachine,
+ }),
+ organizationApi.roleApi.create({
+ name: generateTestName(),
+ type: RoleType.MachineToMachine,
+ }),
]);
const roleIds = roles.map(({ id }) => id);
await organizationApi.addApplicationRoles(organizationId, app.id, roleIds);
@@ -202,7 +209,10 @@ devFeatureTest.describe('organization application APIs', () => {
it('should be able to add and delete organization application role', async () => {
const organization = await organizationApi.create({ name: 'test' });
- const role = await organizationApi.roleApi.create({ name: `test-${generateTestName()}` });
+ const role = await organizationApi.roleApi.create({
+ name: `test-${generateTestName()}`,
+ type: RoleType.MachineToMachine,
+ });
const application = await createApplication(
generateTestName(),
ApplicationType.MachineToMachine
@@ -227,5 +237,27 @@ devFeatureTest.describe('organization application APIs', () => {
assert(response instanceof HTTPError);
expect(response.response.status).toBe(422);
});
+
+ it('should fail when try to add role that is not machine-to-machine type', async () => {
+ const organization = await organizationApi.create({ name: 'test' });
+ const role = await organizationApi.roleApi.create({
+ name: `test-${generateTestName()}`,
+ type: RoleType.User,
+ });
+ const application = await createApplication(
+ generateTestName(),
+ ApplicationType.MachineToMachine
+ );
+ await organizationApi.applications.add(organization.id, [application.id]);
+
+ const response = await organizationApi
+ .addApplicationRoles(organization.id, application.id, [role.id])
+ .catch((error: unknown) => error);
+ assert(response instanceof HTTPError);
+ expect(response.response.status).toBe(422);
+ expect(await response.response.json()).toMatchObject(
+ expect.objectContaining({ code: 'entity.db_constraint_violated' })
+ );
+ });
});
});
diff --git a/packages/integration-tests/src/tests/api/organization/organization-user.test.ts b/packages/integration-tests/src/tests/api/organization/organization-user.test.ts
index a7d882db3..91801d43a 100644
--- a/packages/integration-tests/src/tests/api/organization/organization-user.test.ts
+++ b/packages/integration-tests/src/tests/api/organization/organization-user.test.ts
@@ -1,10 +1,11 @@
import assert from 'node:assert';
+import { RoleType } from '@logto/schemas';
import { HTTPError } from 'ky';
import { OrganizationApiTest } from '#src/helpers/organization.js';
import { UserApiTest } from '#src/helpers/user.js';
-import { generateTestName } from '#src/utils.js';
+import { devFeatureTest, generateTestName } from '#src/utils.js';
describe('organization user APIs', () => {
describe('organization get users', () => {
@@ -287,6 +288,25 @@ describe('organization user APIs', () => {
expect.objectContaining({ code: 'entity.not_found' })
);
});
+
+ devFeatureTest.it('should fail when try to add role that is not user type', async () => {
+ const organization = await organizationApi.create({ name: 'test' });
+ const user = await userApi.create({ username: generateTestName() });
+ const role = await roleApi.create({
+ name: generateTestName(),
+ type: RoleType.MachineToMachine,
+ });
+
+ await organizationApi.addUsers(organization.id, [user.id]);
+ const response = await organizationApi
+ .addUserRoles(organization.id, user.id, [role.id])
+ .catch((error: unknown) => error);
+ assert(response instanceof HTTPError);
+ expect(response.response.status).toBe(422);
+ expect(await response.response.json()).toMatchObject(
+ expect.objectContaining({ code: 'entity.db_constraint_violated' })
+ );
+ });
});
describe('organization - user - organization role - organization scopes relation', () => {
diff --git a/packages/integration-tests/src/tests/console/rbac/helper.ts b/packages/integration-tests/src/tests/console/rbac/helper.ts
index 653833f43..92b35b389 100644
--- a/packages/integration-tests/src/tests/console/rbac/helper.ts
+++ b/packages/integration-tests/src/tests/console/rbac/helper.ts
@@ -46,7 +46,7 @@ export const createM2mRoleAndAssignPermissions = async (
await expectModalWithTitle(page, 'Create role');
await expect(page).toClick('div[class*=radioGroup][class$=roleTypes] div[class$=content]', {
- text: 'Machine-to-machine role',
+ text: 'Machine-to-machine',
});
await expect(page).toFillForm('.ReactModalPortal form', {
diff --git a/packages/phrases/src/locales/de/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/de/translation/admin-console/organization-role-details.ts
index 3c0d2f296..04152b11f 100644
--- a/packages/phrases/src/locales/de/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/de/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Organisationsrollendetails',
back_to_org_roles: 'Zurück zu den Organisationsrollen',
- org_role: 'Org-Rolle',
delete_confirm:
'Dadurch werden die mit dieser Rolle verbundenen Berechtigungen von den betroffenen Benutzern entfernt und die Beziehungen zwischen Organisationsrollen, Mitgliedern in der Organisation und Organisationsberechtigungen gelöscht.',
deleted: 'Die Organisationsrolle {{name}} wurde erfolgreich gelöscht.',
diff --git a/packages/phrases/src/locales/de/translation/admin-console/roles.ts b/packages/phrases/src/locales/de/translation/admin-console/roles.ts
index acd19b414..a214a862a 100644
--- a/packages/phrases/src/locales/de/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/de/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Rolle erstellen',
role_name: 'Rollenname',
role_type: 'Rollenart',
- type_user: 'Benutzerrolle',
- type_machine_to_machine: 'Rolle von Maschine zu Maschine',
role_description: 'Beschreibung',
role_name_placeholder: 'Geben Sie Ihren Rollennamen ein',
role_description_placeholder: 'Geben Sie Ihre Rollenbeschreibung ein',
diff --git a/packages/phrases/src/locales/en/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/en/translation/admin-console/organization-role-details.ts
index bace0551e..2d9b45fac 100644
--- a/packages/phrases/src/locales/en/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/en/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Organization role details',
back_to_org_roles: 'Back to organization roles',
- org_role: 'Organization role',
delete_confirm:
'Doing so will remove the permissions associated with this role from the affected users and delete the relations among organization roles, members in the organization, and organization permissions.',
deleted: 'Organization role {{name}} was successfully deleted.',
diff --git a/packages/phrases/src/locales/en/translation/admin-console/organization-template.ts b/packages/phrases/src/locales/en/translation/admin-console/organization-template.ts
index 75b47061a..62d555799 100644
--- a/packages/phrases/src/locales/en/translation/admin-console/organization-template.ts
+++ b/packages/phrases/src/locales/en/translation/admin-console/organization-template.ts
@@ -8,14 +8,16 @@ const organization_template = {
create_title: 'Create organization role',
role_column: 'Organization role',
permissions_column: 'Permissions',
+ type_column: 'Role type',
placeholder_title: 'Organization role',
placeholder_description:
'Organization role is a grouping of permissions that can be assigned to users. The permissions must come from the predefined organization permissions.',
create_modal: {
title: 'Create organization role',
create: 'Create role',
- name_field: 'Role name',
- description_field: 'Description',
+ name: 'Role name',
+ description: 'Description',
+ type: 'Role type',
created: 'Organization role {{name}} has been successfully created.',
},
},
diff --git a/packages/phrases/src/locales/en/translation/admin-console/roles.ts b/packages/phrases/src/locales/en/translation/admin-console/roles.ts
index 6c40142aa..2f120b082 100644
--- a/packages/phrases/src/locales/en/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/en/translation/admin-console/roles.ts
@@ -6,8 +6,8 @@ const roles = {
create: 'Create role',
role_name: 'Role name',
role_type: 'Role type',
- type_user: 'User role',
- type_machine_to_machine: 'Machine-to-machine role',
+ type_user: 'User',
+ type_machine_to_machine: 'Machine-to-machine',
role_description: 'Description',
role_name_placeholder: 'Enter your role name',
role_description_placeholder: 'Enter your role description',
diff --git a/packages/phrases/src/locales/es/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/es/translation/admin-console/organization-role-details.ts
index 8da044728..705833aaf 100644
--- a/packages/phrases/src/locales/es/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/es/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Detalles del rol de la organización',
back_to_org_roles: 'Volver a los roles de la organización',
- org_role: 'Rol de la organización',
delete_confirm:
'Al hacerlo, se eliminarán los permisos asociados con este rol de los usuarios afectados y se borrarán las relaciones entre roles de organización, miembros en la organización y permisos de organización.',
deleted: 'El rol de organización {{name}} se eliminó con éxito.',
diff --git a/packages/phrases/src/locales/es/translation/admin-console/roles.ts b/packages/phrases/src/locales/es/translation/admin-console/roles.ts
index fea4bff00..30b2030be 100644
--- a/packages/phrases/src/locales/es/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/es/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Crear Rol',
role_name: 'Nombre de rol',
role_type: 'Tipo de rol',
- type_user: 'Rol de usuario',
- type_machine_to_machine: 'Rol de máquina a máquina',
role_description: 'Descripción',
role_name_placeholder: 'Ingrese el nombre de su rol',
role_description_placeholder: 'Ingrese la descripción de su rol',
diff --git a/packages/phrases/src/locales/fr/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/fr/translation/admin-console/organization-role-details.ts
index 36c932c9f..bcac051fa 100644
--- a/packages/phrases/src/locales/fr/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/fr/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: "Détails du rôle de l'organisation",
back_to_org_roles: "Retour aux rôles de l'organisation",
- org_role: "Rôle de l'organisation",
delete_confirm:
"Cela supprimera les autorisations associées à ce rôle des utilisateurs concernés et supprimera les relations entre les rôles de l'organisation, les membres de l'organisation et les autorisations de l'organisation.",
deleted: "Le rôle de l'organisation {{name}} a été supprimé avec succès.",
diff --git a/packages/phrases/src/locales/fr/translation/admin-console/roles.ts b/packages/phrases/src/locales/fr/translation/admin-console/roles.ts
index 81beb9aea..00db35af7 100644
--- a/packages/phrases/src/locales/fr/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/fr/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Créer un rôle',
role_name: 'Nom du rôle',
role_type: 'Type de rôle',
- type_user: 'Rôle utilisateur',
- type_machine_to_machine: 'Rôle de machine à machine',
role_description: 'Description',
role_name_placeholder: 'Entrez le nom de votre rôle',
role_description_placeholder: 'Entrez la description de votre rôle',
diff --git a/packages/phrases/src/locales/it/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/it/translation/admin-console/organization-role-details.ts
index 5a20b180c..29051a37e 100644
--- a/packages/phrases/src/locales/it/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/it/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: "Dettagli del ruolo dell'organizzazione",
back_to_org_roles: "Torna ai ruoli dell'organizzazione",
- org_role: "Ruolo dell'organizzazione",
delete_confirm:
"Facendo ciò, verranno rimossi i permessi associati a questo ruolo dagli utenti interessati e verranno eliminati i rapporti tra ruoli organizzativi, membri nell'organizzazione e permessi dell'organizzazione.",
deleted: "Il ruolo dell'organizzazione {{name}} è stato cancellato con successo.",
diff --git a/packages/phrases/src/locales/it/translation/admin-console/roles.ts b/packages/phrases/src/locales/it/translation/admin-console/roles.ts
index 6541e4eab..91c2f0dcc 100644
--- a/packages/phrases/src/locales/it/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/it/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Crea Ruolo',
role_name: 'Nome ruolo',
role_type: 'Tipo ruolo',
- type_user: 'Ruolo utente',
- type_machine_to_machine: 'Ruolo da macchina a macchina',
role_description: 'Descrizione',
role_name_placeholder: 'Inserisci il nome del tuo ruolo',
role_description_placeholder: 'Inserisci la descrizione del tuo ruolo',
diff --git a/packages/phrases/src/locales/ja/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/ja/translation/admin-console/organization-role-details.ts
index 4a2c15f1a..7e290d2f2 100644
--- a/packages/phrases/src/locales/ja/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/ja/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: '組織の役割の詳細',
back_to_org_roles: '組織の役割に戻る',
- org_role: '組織の役割',
delete_confirm:
'これにより、関連するユーザーからこのロールに関連付けられた権限が削除され、組織の役割、組織のメンバー、および組織の権限間の関係が削除されます。',
deleted: '組織の役割{{name}}が正常に削除されました。',
diff --git a/packages/phrases/src/locales/ja/translation/admin-console/roles.ts b/packages/phrases/src/locales/ja/translation/admin-console/roles.ts
index 740aa9db4..2ad2910d5 100644
--- a/packages/phrases/src/locales/ja/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/ja/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'ロールを作成する',
role_name: '役割名',
role_type: '役割タイプ',
- type_user: 'ユーザーの役割',
- type_machine_to_machine: 'マシン間の役割',
role_description: '説明',
role_name_placeholder: 'ロールの名前を入力してください',
role_description_placeholder: 'ロールの説明を入力してください',
diff --git a/packages/phrases/src/locales/ko/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/ko/translation/admin-console/organization-role-details.ts
index c7e93a2b7..370bc7435 100644
--- a/packages/phrases/src/locales/ko/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/ko/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: '조직 역할 세부 정보',
back_to_org_roles: '조직 역할로 돌아가기',
- org_role: '조직 역할',
delete_confirm:
'이렇게 하면 해당 역할과 관련된 사용자의 권한이 제거되고 조직 역할, 조직 구성원 및 조직 권한 간의 관계가 삭제됩니다.',
deleted: '조직 역할 {{name}} 이(가) 성공적으로 삭제되었습니다.',
diff --git a/packages/phrases/src/locales/ko/translation/admin-console/roles.ts b/packages/phrases/src/locales/ko/translation/admin-console/roles.ts
index 1f0da4b2e..fdbd0e51c 100644
--- a/packages/phrases/src/locales/ko/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/ko/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: '역할 생성',
role_name: '역할 이름',
role_type: '역할 유형',
- type_user: '사용자 역할',
- type_machine_to_machine: '기계 간 역할',
role_description: '설명',
role_name_placeholder: '역할 이름을 입력하세요',
role_description_placeholder: '역할 설명을 입력하세요',
diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/organization-role-details.ts
index d9b71f053..5317294cc 100644
--- a/packages/phrases/src/locales/pl-pl/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Szczegóły roli organizacji',
back_to_org_roles: 'Powrót do ról organizacyjnych',
- org_role: 'Rola organizacji',
delete_confirm:
'W wyniku tego zostaną usunięte uprawnienia związane z tą rolą od dotkniętych użytkowników i zostaną usunięte związki między rolami organizacyjnymi, członkami organizacji a uprawnieniami organizacji.',
deleted: 'Rola organizacji {{name}} została pomyślnie usunięta.',
diff --git a/packages/phrases/src/locales/pl-pl/translation/admin-console/roles.ts b/packages/phrases/src/locales/pl-pl/translation/admin-console/roles.ts
index 96dfb0d30..989a0726a 100644
--- a/packages/phrases/src/locales/pl-pl/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/pl-pl/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Utwórz rolę',
role_name: 'Nazwa roli',
role_type: 'Typ roli',
- type_user: 'Rola użytkownika',
- type_machine_to_machine: 'Rola maszyny do maszyny',
role_description: 'Opis',
role_name_placeholder: 'Wprowadź nazwę swojej roli',
role_description_placeholder: 'Wprowadź opis swojej roli',
diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/organization-role-details.ts
index 9b7a502d4..769f5b231 100644
--- a/packages/phrases/src/locales/pt-br/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/pt-br/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Detalhes da função da organização',
back_to_org_roles: 'Voltar para os papéis da organização',
- org_role: 'Função da organização',
delete_confirm:
'Ao fazer isso, os privilégios associados a esta função serão removidos dos usuários afetados e as relações entre funções da organização, membros na organização e permissões da organização serão excluídas.',
deleted: 'O papel da organização {{name}} foi excluído com sucesso.',
diff --git a/packages/phrases/src/locales/pt-br/translation/admin-console/roles.ts b/packages/phrases/src/locales/pt-br/translation/admin-console/roles.ts
index f79cb268b..53e3ed02f 100644
--- a/packages/phrases/src/locales/pt-br/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/pt-br/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Criar função',
role_name: 'Nome da função',
role_type: 'Tipo de função',
- type_user: 'Função do usuário',
- type_machine_to_machine: 'Função de máquina para máquina',
role_description: 'Descrição',
role_name_placeholder: 'Insira o nome da sua função',
role_description_placeholder: 'Insira a descrição da sua função',
diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/organization-role-details.ts
index 488bc5c0e..a4c0b5e7a 100644
--- a/packages/phrases/src/locales/pt-pt/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Detalhes da função da organização',
back_to_org_roles: 'Voltar aos papéis da organização',
- org_role: 'Função da organização',
delete_confirm:
'Ao fazê-lo, serão removidas as permissões associadas a esta função dos utilizadores afetados e serão eliminadas as relações entre funções da organização, membros na organização e permissões da organização.',
deleted: 'O papel da organização {{name}} foi eliminado com sucesso.',
diff --git a/packages/phrases/src/locales/pt-pt/translation/admin-console/roles.ts b/packages/phrases/src/locales/pt-pt/translation/admin-console/roles.ts
index e92c0256e..269c956a4 100644
--- a/packages/phrases/src/locales/pt-pt/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/pt-pt/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Criar papel',
role_name: 'Nome do papel',
role_type: 'Tipo de papel',
- type_user: 'Função de usuário',
- type_machine_to_machine: 'Função de máquina para máquina',
role_description: 'Descrição',
role_name_placeholder: 'Digite o nome do papel',
role_description_placeholder: 'Digite a descrição do papel',
diff --git a/packages/phrases/src/locales/ru/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/ru/translation/admin-console/organization-role-details.ts
index b22af529d..3594debd2 100644
--- a/packages/phrases/src/locales/ru/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/ru/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Детали роли организации',
back_to_org_roles: 'Вернуться к ролям в организации',
- org_role: 'Роль организации',
delete_confirm:
'При этом будут удалены разрешения, связанные с этой ролью, у затронутых пользователей, и будут удалены связи между ролями организации, членами организации и правами организации.',
deleted: 'Роль в организации {{name}} была успешно удалена.',
diff --git a/packages/phrases/src/locales/ru/translation/admin-console/roles.ts b/packages/phrases/src/locales/ru/translation/admin-console/roles.ts
index 012ea6627..257d19583 100644
--- a/packages/phrases/src/locales/ru/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/ru/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Создать роль',
role_name: 'Имя роли',
role_type: 'Тип роли',
- type_user: 'Роль пользователя',
- type_machine_to_machine: 'Роль машина-машина',
role_description: 'Описание',
role_name_placeholder: 'Введите название роли',
role_description_placeholder: 'Введите описание роли',
diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/organization-role-details.ts
index 12c26925f..e6d42de8d 100644
--- a/packages/phrases/src/locales/tr-tr/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: 'Kuruluş rolü ayrıntıları',
back_to_org_roles: 'Organizasyon rollerine geri dön',
- org_role: 'Kuruluş rolü',
delete_confirm:
'Bunu yapmak, etkilenen kullanıcılardan bu role ilişkilendirilen izinleri kaldıracak ve organizasyon rolleri, organizasyon üyeleri ve organizasyon izinleri arasındaki ilişkileri silecektir.',
deleted: 'Kuruluş rolü {{name}} başarıyla silindi.',
diff --git a/packages/phrases/src/locales/tr-tr/translation/admin-console/roles.ts b/packages/phrases/src/locales/tr-tr/translation/admin-console/roles.ts
index 3b4cafd33..3f713908b 100644
--- a/packages/phrases/src/locales/tr-tr/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/tr-tr/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: 'Rol Oluştur',
role_name: 'Rol adı',
role_type: 'Rol tipi',
- type_user: 'Kullanıcı rolü',
- type_machine_to_machine: 'Makineden makineye rol',
role_description: 'Açıklama',
role_name_placeholder: 'Rol adınızı girin',
role_description_placeholder: 'Rol açıklamanızı girin',
diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/organization-role-details.ts
index d657f860d..38a8fa767 100644
--- a/packages/phrases/src/locales/zh-cn/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: '组织角色详情',
back_to_org_roles: '返回组织角色',
- org_role: '组织角色',
delete_confirm:
'这样做将从受影响的用户中删除与此角色关联的权限,并删除组织角色、组织成员和组织权限之间的关系。',
deleted: '组织角色 {{name}} 已成功删除。',
diff --git a/packages/phrases/src/locales/zh-cn/translation/admin-console/roles.ts b/packages/phrases/src/locales/zh-cn/translation/admin-console/roles.ts
index c71ae4ee3..cb744cfb4 100644
--- a/packages/phrases/src/locales/zh-cn/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/zh-cn/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: '创建角色',
role_name: '角色名称',
role_type: '角色类型',
- type_user: '用户角色',
- type_machine_to_machine: '机器对机器角色',
role_description: '描述',
role_name_placeholder: '输入你的角色名称',
role_description_placeholder: '输入你的角色描述',
diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/organization-role-details.ts
index b11cc005a..9bdf9b5df 100644
--- a/packages/phrases/src/locales/zh-hk/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: '組織角色詳情',
back_to_org_roles: '返回組織角色',
- org_role: '組織角色',
delete_confirm:
'這樣做將會從受影響的使用者中移除與此角色相關聯的權限,並刪除組織角色、組織成員和組織權限之間的關係。',
deleted: '組織角色 {{name}} 已成功刪除。',
diff --git a/packages/phrases/src/locales/zh-hk/translation/admin-console/roles.ts b/packages/phrases/src/locales/zh-hk/translation/admin-console/roles.ts
index 0c61476df..b4661638d 100644
--- a/packages/phrases/src/locales/zh-hk/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/zh-hk/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: '創建角色',
role_name: '角色名稱',
role_type: '角色類型',
- type_user: '用戶角色',
- type_machine_to_machine: '機器對機器角色',
role_description: '描述',
role_name_placeholder: '輸入你的角色名稱',
role_description_placeholder: '輸入你的角色描述',
diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/organization-role-details.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/organization-role-details.ts
index bdce17089..c9d187a47 100644
--- a/packages/phrases/src/locales/zh-tw/translation/admin-console/organization-role-details.ts
+++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/organization-role-details.ts
@@ -1,7 +1,6 @@
const organization_role_details = {
page_title: '組織角色詳情',
back_to_org_roles: '返回組織角色',
- org_role: '組織角色',
delete_confirm:
'這樣做將會從受影響的使用者中移除與此角色相關聯的權限,並刪除組織角色、組織成員和組織權限之間的關係。',
deleted: '組織角色 {{name}} 已成功刪除。',
diff --git a/packages/phrases/src/locales/zh-tw/translation/admin-console/roles.ts b/packages/phrases/src/locales/zh-tw/translation/admin-console/roles.ts
index 7fd35c34a..0fefcb088 100644
--- a/packages/phrases/src/locales/zh-tw/translation/admin-console/roles.ts
+++ b/packages/phrases/src/locales/zh-tw/translation/admin-console/roles.ts
@@ -6,8 +6,6 @@ const roles = {
create: '建立角色',
role_name: '角色名稱',
role_type: '角色類型',
- type_user: '使用者角色',
- type_machine_to_machine: '機器對機器角色',
role_description: '描述',
role_name_placeholder: '輸入你的角色名稱',
role_description_placeholder: '輸入你的角色描述',
diff --git a/packages/schemas/alterations/next-1719014832-organization-role-types.ts b/packages/schemas/alterations/next-1719014832-organization-role-types.ts
new file mode 100644
index 000000000..b46ecc7f7
--- /dev/null
+++ b/packages/schemas/alterations/next-1719014832-organization-role-types.ts
@@ -0,0 +1,35 @@
+import { sql } from '@silverhand/slonik';
+
+import type { AlterationScript } from '../lib/types/alteration.js';
+
+const alteration: AlterationScript = {
+ up: async (pool) => {
+ await pool.query(sql`
+ alter table organization_roles
+ add column type role_type not null default 'User';
+ create function check_organization_role_type(role_id varchar(21), target_type role_type) returns boolean as
+ $$ begin
+ return (select type from organization_roles where id = role_id) = target_type;
+ end; $$ language plpgsql;
+ alter table organization_role_user_relations
+ add constraint organization_role_user_relations__role_type
+ check (check_organization_role_type(organization_role_id, 'User'));
+ alter table organization_role_application_relations
+ add constraint organization_role_application_relations__role_type
+ check (check_organization_role_type(organization_role_id, 'MachineToMachine'));
+ `);
+ },
+ down: async (pool) => {
+ await pool.query(sql`
+ alter table organization_role_application_relations
+ drop constraint organization_role_application_relations__role_type;
+ alter table organization_role_user_relations
+ drop constraint organization_role_user_relations__role_type;
+ alter table organization_roles
+ drop column type;
+ drop function check_organization_role_type;
+ `);
+ },
+};
+
+export default alteration;
diff --git a/packages/schemas/src/types/role.ts b/packages/schemas/src/types/role.ts
index a2280e33f..aaf225698 100644
--- a/packages/schemas/src/types/role.ts
+++ b/packages/schemas/src/types/role.ts
@@ -1,4 +1,4 @@
-import type { Role } from '../db-entries/index.js';
+import { RoleType, type Role } from '../db-entries/index.js';
import { type FeaturedApplication } from './application.js';
import { type FeaturedUser } from './user.js';
@@ -9,3 +9,9 @@ export type RoleResponse = Role & {
applicationsCount: number;
featuredApplications: FeaturedApplication[];
};
+
+/** The role type to i18n key mapping. */
+export const roleTypeToKey = Object.freeze({
+ [RoleType.User]: 'user',
+ [RoleType.MachineToMachine]: 'machine_to_machine',
+} as const satisfies Record);
diff --git a/packages/schemas/src/types/tenant-organization.ts b/packages/schemas/src/types/tenant-organization.ts
index beaf4524a..b6de216e8 100644
--- a/packages/schemas/src/types/tenant-organization.ts
+++ b/packages/schemas/src/types/tenant-organization.ts
@@ -8,6 +8,7 @@
*/
import {
+ RoleType,
type CreateOrganization,
type OrganizationRole,
type OrganizationScope,
@@ -147,6 +148,7 @@ const tenantRoleDescriptions: Readonly> = Object.free
* id: 'collaborator',
* name: 'collaborator',
* description: 'Collaborator of the tenant, who has permissions to operate the tenant data, but not the tenant settings.',
+ * type: RoleType.User,
* });
* ```
*
@@ -158,6 +160,7 @@ export const getTenantRole = (role: TenantRole): Readonly =>
id: role,
name: role,
description: tenantRoleDescriptions[role],
+ type: RoleType.User,
});
/**
diff --git a/packages/schemas/tables/organization_role_application_relations.sql b/packages/schemas/tables/organization_role_application_relations.sql
index 05f27a52a..c3acc7b94 100644
--- a/packages/schemas/tables/organization_role_application_relations.sql
+++ b/packages/schemas/tables/organization_role_application_relations.sql
@@ -12,5 +12,7 @@ create table organization_role_application_relations (
/** Application's roles in an organization should be synchronized with the application's membership in the organization. */
foreign key (tenant_id, organization_id, application_id)
references organization_application_relations (tenant_id, organization_id, application_id)
- on update cascade on delete cascade
+ on update cascade on delete cascade,
+ constraint organization_role_application_relations__role_type
+ check (check_organization_role_type(organization_role_id, 'MachineToMachine'))
);
diff --git a/packages/schemas/tables/organization_role_user_relations.sql b/packages/schemas/tables/organization_role_user_relations.sql
index 8a6cdc6a7..a432704ef 100644
--- a/packages/schemas/tables/organization_role_user_relations.sql
+++ b/packages/schemas/tables/organization_role_user_relations.sql
@@ -12,5 +12,7 @@ create table organization_role_user_relations (
/** User's roles in an organization should be synchronized with the user's membership in the organization. */
foreign key (tenant_id, organization_id, user_id)
references organization_user_relations (tenant_id, organization_id, user_id)
- on update cascade on delete cascade
+ on update cascade on delete cascade,
+ constraint organization_role_user_relations__role_type
+ check (check_organization_role_type(organization_role_id, 'User'))
);
diff --git a/packages/schemas/tables/organization_roles.sql b/packages/schemas/tables/organization_roles.sql
index 85b19a7ed..3d7b86efa 100644
--- a/packages/schemas/tables/organization_roles.sql
+++ b/packages/schemas/tables/organization_roles.sql
@@ -1,4 +1,4 @@
-/* init_order = 1 */
+/* init_order = 1.1 */
/** The roles defined by the organization template. */
create table organization_roles (
@@ -10,6 +10,8 @@ create table organization_roles (
name varchar(128) not null,
/** A brief description of the organization role. */
description varchar(256),
+ /** The type of the organization role. Same as the `type` field in the `roles` table. */
+ type role_type not null default 'User',
primary key (id),
constraint organization_roles__name
unique (tenant_id, name)
@@ -17,3 +19,8 @@ create table organization_roles (
create index organization_roles__id
on organization_roles (tenant_id, id);
+
+create function check_organization_role_type(role_id varchar(21), target_type role_type) returns boolean as
+$$ begin
+ return (select type from organization_roles where id = role_id) = target_type;
+end; $$ language plpgsql;