[1/3] Rename permissionFlag to rolePermissionFlag + add permissionFlag catalog/backfill (#20481)
Split of #20377. ## Summary This PR separates available permission flags from per-role permission flag grants. Previously, `core.permissionFlag` stored the role assignment directly: `roleId + flag`. This PR renames that legacy grant table to `core.rolePermissionFlag`, then recreates `core.permissionFlag` as the catalog of available permission flags. ## What changed - Rename the existing `core.permissionFlag` grant table to `core.rolePermissionFlag`. - Add the new syncable `core.permissionFlag` catalog entity with key, label, description, icon, permission type, relevance flags, and custom/standard metadata. - Add stable `SystemPermissionFlag` universal identifiers for the built-in `PermissionFlagType` values. - Seed the standard permission flags for every workspace under the Twenty standard application. - Backfill existing role grants: - create missing catalog rows for existing grant keys, - add `rolePermissionFlag.permissionFlagId`, - migrate grants from the old string `flag` column to the new catalog FK, - replace the old `(flag, roleId)` uniqueness with `(permissionFlagId, roleId)`. - Rewire role permission flag caches, permission checks, role DTO mapping, and `upsertPermissionFlags` to resolve through the catalog. - Keep the existing public role permission API shape: product/app surfaces still talk about `permissionFlags` and return `{ id, roleId, flag }`. - Update metadata flat-entity machinery, migration builders, validators, action handlers, snapshots, generated schemas, docs, and app fixtures for the new `permissionFlag` / `rolePermissionFlag` split. ## Behavior after this PR - Existing permission flag grants keep working. - Existing GraphQL role permission flows keep the same public naming. - Standard permission flags are represented as catalog rows. - Permission checks now compare grants through catalog universal identifiers instead of the legacy `flag` column. - Workspace deletion cleanup now verifies both `permissionFlag` and `rolePermissionFlag`. ## What is not in this PR - Public GraphQL CRUD for custom permission flags. - App manifest support for declaring new custom permission flags. - Frontend UI for creating or assigning custom permission flags beyond the existing role permission flow. --------- Co-authored-by: Weiko <corentin@twenty.com>
This commit is contained in:
@@ -244,7 +244,7 @@ type FieldPermission {
|
||||
canUpdateFieldValue: Boolean
|
||||
}
|
||||
|
||||
type PermissionFlag {
|
||||
type RolePermissionFlag {
|
||||
id: UUID!
|
||||
roleId: UUID!
|
||||
flag: PermissionFlagType!
|
||||
@@ -276,7 +276,7 @@ type Role {
|
||||
canUpdateAllObjectRecords: Boolean!
|
||||
canSoftDeleteAllObjectRecords: Boolean!
|
||||
canDestroyAllObjectRecords: Boolean!
|
||||
permissionFlags: [PermissionFlag!]
|
||||
permissionFlags: [RolePermissionFlag!]
|
||||
objectPermissions: [ObjectPermission!]
|
||||
fieldPermissions: [FieldPermission!]
|
||||
rowLevelPermissionPredicates: [RowLevelPermissionPredicate!]
|
||||
@@ -2884,6 +2884,7 @@ enum AllMetadataName {
|
||||
pageLayoutTab
|
||||
commandMenuItem
|
||||
navigationMenuItem
|
||||
rolePermissionFlag
|
||||
permissionFlag
|
||||
objectPermission
|
||||
fieldPermission
|
||||
@@ -3237,7 +3238,7 @@ type Mutation {
|
||||
updateOneRole(updateRoleInput: UpdateRoleInput!): Role!
|
||||
deleteOneRole(roleId: UUID!): String!
|
||||
upsertObjectPermissions(upsertObjectPermissionsInput: UpsertObjectPermissionsInput!): [ObjectPermission!]!
|
||||
upsertPermissionFlags(upsertPermissionFlagsInput: UpsertPermissionFlagsInput!): [PermissionFlag!]!
|
||||
upsertPermissionFlags(upsertPermissionFlagsInput: UpsertPermissionFlagsInput!): [RolePermissionFlag!]!
|
||||
upsertFieldPermissions(upsertFieldPermissionsInput: UpsertFieldPermissionsInput!): [FieldPermission!]!
|
||||
upsertRowLevelPermissionPredicates(input: UpsertRowLevelPermissionPredicatesInput!): UpsertRowLevelPermissionPredicatesResult!
|
||||
assignRoleToAgent(agentId: UUID!, roleId: UUID!): Boolean!
|
||||
|
||||
@@ -190,11 +190,11 @@ export interface FieldPermission {
|
||||
__typename: 'FieldPermission'
|
||||
}
|
||||
|
||||
export interface PermissionFlag {
|
||||
export interface RolePermissionFlag {
|
||||
id: Scalars['UUID']
|
||||
roleId: Scalars['UUID']
|
||||
flag: PermissionFlagType
|
||||
__typename: 'PermissionFlag'
|
||||
__typename: 'RolePermissionFlag'
|
||||
}
|
||||
|
||||
export interface ApiKeyForRole {
|
||||
@@ -224,7 +224,7 @@ export interface Role {
|
||||
canUpdateAllObjectRecords: Scalars['Boolean']
|
||||
canSoftDeleteAllObjectRecords: Scalars['Boolean']
|
||||
canDestroyAllObjectRecords: Scalars['Boolean']
|
||||
permissionFlags?: PermissionFlag[]
|
||||
permissionFlags?: RolePermissionFlag[]
|
||||
objectPermissions?: ObjectPermission[]
|
||||
fieldPermissions?: FieldPermission[]
|
||||
rowLevelPermissionPredicates?: RowLevelPermissionPredicate[]
|
||||
@@ -2516,7 +2516,7 @@ export interface CollectionHash {
|
||||
__typename: 'CollectionHash'
|
||||
}
|
||||
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'permissionFlag' | 'objectPermission' | 'fieldPermission' | 'frontComponent' | 'webhook' | 'applicationVariable' | 'connectionProvider'
|
||||
export type AllMetadataName = 'fieldMetadata' | 'objectMetadata' | 'view' | 'viewField' | 'viewFieldGroup' | 'viewGroup' | 'viewSort' | 'rowLevelPermissionPredicate' | 'rowLevelPermissionPredicateGroup' | 'viewFilterGroup' | 'index' | 'logicFunction' | 'viewFilter' | 'role' | 'roleTarget' | 'agent' | 'skill' | 'pageLayout' | 'pageLayoutWidget' | 'pageLayoutTab' | 'commandMenuItem' | 'navigationMenuItem' | 'rolePermissionFlag' | 'permissionFlag' | 'objectPermission' | 'fieldPermission' | 'frontComponent' | 'webhook' | 'applicationVariable' | 'connectionProvider'
|
||||
|
||||
export interface MinimalObjectMetadata {
|
||||
id: Scalars['UUID']
|
||||
@@ -2772,7 +2772,7 @@ export interface Mutation {
|
||||
updateOneRole: Role
|
||||
deleteOneRole: Scalars['String']
|
||||
upsertObjectPermissions: ObjectPermission[]
|
||||
upsertPermissionFlags: PermissionFlag[]
|
||||
upsertPermissionFlags: RolePermissionFlag[]
|
||||
upsertFieldPermissions: FieldPermission[]
|
||||
upsertRowLevelPermissionPredicates: UpsertRowLevelPermissionPredicatesResult
|
||||
assignRoleToAgent: Scalars['Boolean']
|
||||
@@ -3072,7 +3072,7 @@ export interface FieldPermissionGenqlSelection{
|
||||
__scalar?: boolean | number
|
||||
}
|
||||
|
||||
export interface PermissionFlagGenqlSelection{
|
||||
export interface RolePermissionFlagGenqlSelection{
|
||||
id?: boolean | number
|
||||
roleId?: boolean | number
|
||||
flag?: boolean | number
|
||||
@@ -3108,7 +3108,7 @@ export interface RoleGenqlSelection{
|
||||
canUpdateAllObjectRecords?: boolean | number
|
||||
canSoftDeleteAllObjectRecords?: boolean | number
|
||||
canDestroyAllObjectRecords?: boolean | number
|
||||
permissionFlags?: PermissionFlagGenqlSelection
|
||||
permissionFlags?: RolePermissionFlagGenqlSelection
|
||||
objectPermissions?: ObjectPermissionGenqlSelection
|
||||
fieldPermissions?: FieldPermissionGenqlSelection
|
||||
rowLevelPermissionPredicates?: RowLevelPermissionPredicateGenqlSelection
|
||||
@@ -5847,7 +5847,7 @@ export interface MutationGenqlSelection{
|
||||
updateOneRole?: (RoleGenqlSelection & { __args: {updateRoleInput: UpdateRoleInput} })
|
||||
deleteOneRole?: { __args: {roleId: Scalars['UUID']} }
|
||||
upsertObjectPermissions?: (ObjectPermissionGenqlSelection & { __args: {upsertObjectPermissionsInput: UpsertObjectPermissionsInput} })
|
||||
upsertPermissionFlags?: (PermissionFlagGenqlSelection & { __args: {upsertPermissionFlagsInput: UpsertPermissionFlagsInput} })
|
||||
upsertPermissionFlags?: (RolePermissionFlagGenqlSelection & { __args: {upsertPermissionFlagsInput: UpsertPermissionFlagsInput} })
|
||||
upsertFieldPermissions?: (FieldPermissionGenqlSelection & { __args: {upsertFieldPermissionsInput: UpsertFieldPermissionsInput} })
|
||||
upsertRowLevelPermissionPredicates?: (UpsertRowLevelPermissionPredicatesResultGenqlSelection & { __args: {input: UpsertRowLevelPermissionPredicatesInput} })
|
||||
assignRoleToAgent?: { __args: {agentId: Scalars['UUID'], roleId: Scalars['UUID']} }
|
||||
@@ -6426,10 +6426,10 @@ export interface LogicFunctionLogsInput {applicationId?: (Scalars['UUID'] | null
|
||||
|
||||
|
||||
|
||||
const PermissionFlag_possibleTypes: string[] = ['PermissionFlag']
|
||||
export const isPermissionFlag = (obj?: { __typename?: any } | null): obj is PermissionFlag => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isPermissionFlag"')
|
||||
return PermissionFlag_possibleTypes.includes(obj.__typename)
|
||||
const RolePermissionFlag_possibleTypes: string[] = ['RolePermissionFlag']
|
||||
export const isRolePermissionFlag = (obj?: { __typename?: any } | null): obj is RolePermissionFlag => {
|
||||
if (!obj?.__typename) throw new Error('__typename is missing in "isRolePermissionFlag"')
|
||||
return RolePermissionFlag_possibleTypes.includes(obj.__typename)
|
||||
}
|
||||
|
||||
|
||||
@@ -8899,6 +8899,7 @@ export const enumAllMetadataName = {
|
||||
pageLayoutTab: 'pageLayoutTab' as const,
|
||||
commandMenuItem: 'commandMenuItem' as const,
|
||||
navigationMenuItem: 'navigationMenuItem' as const,
|
||||
rolePermissionFlag: 'rolePermissionFlag' as const,
|
||||
permissionFlag: 'permissionFlag' as const,
|
||||
objectPermission: 'objectPermission' as const,
|
||||
fieldPermission: 'fieldPermission' as const,
|
||||
|
||||
@@ -500,7 +500,7 @@ export default {
|
||||
1
|
||||
]
|
||||
},
|
||||
"PermissionFlag": {
|
||||
"RolePermissionFlag": {
|
||||
"id": [
|
||||
3
|
||||
],
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: Roles & Permissions
|
||||
description: Declare what objects and fields your app's logic functions and front components can read and write.
|
||||
icon: "shield-halved"
|
||||
icon: 'shield-halved'
|
||||
---
|
||||
|
||||
A **role** is a permission set: which objects an app can read or write, which fields it can see, and which platform-level capabilities it can use. Every app's logic functions and front components inherit the permissions of the role marked with `defineApplicationRole()` (see [The default function role](#the-default-function-role) below).
|
||||
@@ -40,7 +40,8 @@ export default defineRole({
|
||||
objectUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.universalIdentifier,
|
||||
fieldUniversalIdentifier:
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name.universalIdentifier,
|
||||
STANDARD_OBJECT_UNIVERSAL_IDENTIFIERS.company.fields.name
|
||||
.universalIdentifier,
|
||||
canReadFieldValue: false,
|
||||
canUpdateFieldValue: false,
|
||||
},
|
||||
@@ -80,6 +81,7 @@ export default defineApplicationRole({
|
||||
`defineApplicationRole()` is a thin wrapper around `defineRole()` that flags **the** role used as your application's default at install time. Validation is identical to `defineRole`, but the build pipeline auto-wires its `universalIdentifier` into the application manifest's `defaultRoleUniversalIdentifier` — so you do not need to reference it from [`defineApplication`](/developers/extend/apps/config/application) yourself.
|
||||
|
||||
Notes:
|
||||
|
||||
- Exactly **one** `defineApplicationRole(...)` is allowed per app — the manifest build will fail if it finds more than one.
|
||||
- Use `defineRole()` (not `defineApplicationRole()`) for any **additional** roles your app ships.
|
||||
- Setting `defaultRoleUniversalIdentifier` explicitly on `defineApplication()` is still supported for backward compatibility, but is deprecated in favor of `defineApplicationRole()`.
|
||||
|
||||
File diff suppressed because one or more lines are too long
+1
@@ -30,6 +30,7 @@ export const useMetadataErrorHandler = () => {
|
||||
viewFilter: t`view filter`,
|
||||
index: t`index`,
|
||||
logicFunction: t`logic function`,
|
||||
rolePermissionFlag: t`role permission flag`,
|
||||
permissionFlag: t`permission flag`,
|
||||
objectPermission: t`object permission`,
|
||||
fieldPermission: t`field permission`,
|
||||
|
||||
-9
@@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const PERMISSION_FLAG_FRAGMENT = gql`
|
||||
fragment PermissionFlagFragment on PermissionFlag {
|
||||
id
|
||||
flag
|
||||
roleId
|
||||
}
|
||||
`;
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const ROLE_PERMISSION_FLAG_FRAGMENT = gql`
|
||||
fragment RolePermissionFlagFragment on RolePermissionFlag {
|
||||
id
|
||||
flag
|
||||
roleId
|
||||
}
|
||||
`;
|
||||
+3
-3
@@ -1,15 +1,15 @@
|
||||
import { PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/permissionFlagFragment';
|
||||
import { ROLE_PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/rolePermissionFlagFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPSERT_PERMISSION_FLAGS = gql`
|
||||
${PERMISSION_FLAG_FRAGMENT}
|
||||
${ROLE_PERMISSION_FLAG_FRAGMENT}
|
||||
mutation UpsertPermissionFlags(
|
||||
$upsertPermissionFlagsInput: UpsertPermissionFlagsInput!
|
||||
) {
|
||||
upsertPermissionFlags(
|
||||
upsertPermissionFlagsInput: $upsertPermissionFlagsInput
|
||||
) {
|
||||
...PermissionFlagFragment
|
||||
...RolePermissionFlagFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@@ -2,7 +2,7 @@ import { AGENT_FRAGMENT } from '@/ai/graphql/fragments/agentFragment';
|
||||
import { API_KEY_FOR_ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/apiKeyForRoleFragment';
|
||||
import { FIELD_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/fieldPermissionFragment';
|
||||
import { OBJECT_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/objectPermissionFragment';
|
||||
import { PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/permissionFlagFragment';
|
||||
import { ROLE_PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/rolePermissionFlagFragment';
|
||||
import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment';
|
||||
import { ROW_LEVEL_PERMISSION_PREDICATE_FRAGMENT } from '@/settings/roles/graphql/fragments/rowLevelPermissionPredicateFragment';
|
||||
import { ROW_LEVEL_PERMISSION_PREDICATE_GROUP_FRAGMENT } from '@/settings/roles/graphql/fragments/rowLevelPermissionPredicateGroupFragment';
|
||||
@@ -14,7 +14,7 @@ export const GET_ROLES = gql`
|
||||
${ROLE_FRAGMENT}
|
||||
${AGENT_FRAGMENT}
|
||||
${API_KEY_FOR_ROLE_FRAGMENT}
|
||||
${PERMISSION_FLAG_FRAGMENT}
|
||||
${ROLE_PERMISSION_FLAG_FRAGMENT}
|
||||
${OBJECT_PERMISSION_FRAGMENT}
|
||||
${FIELD_PERMISSION_FRAGMENT}
|
||||
${ROW_LEVEL_PERMISSION_PREDICATE_FRAGMENT}
|
||||
@@ -32,7 +32,7 @@ export const GET_ROLES = gql`
|
||||
...ApiKeyForRoleFragment
|
||||
}
|
||||
permissionFlags {
|
||||
...PermissionFlagFragment
|
||||
...RolePermissionFlagFragment
|
||||
}
|
||||
objectPermissions {
|
||||
...ObjectPermissionFragment
|
||||
|
||||
@@ -6,16 +6,18 @@ import {
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useAtomStateValue } from '@/ui/utilities/state/jotai/hooks/useAtomStateValue';
|
||||
|
||||
export const useHasPermissionFlag = (permissionFlag?: PermissionFlagType) => {
|
||||
export const useHasPermissionFlag = (
|
||||
permissionFlagKey?: PermissionFlagType,
|
||||
) => {
|
||||
const currentWorkspace = useAtomStateValue(currentWorkspaceState);
|
||||
const currentUserWorkspace = useAtomStateValue(currentUserWorkspaceState);
|
||||
|
||||
if (!permissionFlag) {
|
||||
if (!permissionFlagKey) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
permissionFlag === PermissionFlagType.WORKSPACE &&
|
||||
permissionFlagKey === PermissionFlagType.WORKSPACE &&
|
||||
currentWorkspace?.activationStatus ===
|
||||
WorkspaceActivationStatus.PENDING_CREATION
|
||||
) {
|
||||
@@ -23,5 +25,5 @@ export const useHasPermissionFlag = (permissionFlag?: PermissionFlagType) => {
|
||||
}
|
||||
|
||||
const userFlags = currentUserWorkspace?.permissionFlags ?? [];
|
||||
return userFlags.includes(permissionFlag);
|
||||
return userFlags.includes(permissionFlagKey);
|
||||
};
|
||||
|
||||
@@ -92,11 +92,6 @@ export const SettingsApplicationDetails = () => {
|
||||
const displayName =
|
||||
app?.displayName ?? application?.name ?? t`Application details`;
|
||||
const description = app?.description ?? resolvedDescription;
|
||||
const logoUrl =
|
||||
app?.logoUrl ??
|
||||
application?.logo ??
|
||||
application?.applicationRegistration?.logoUrl ??
|
||||
undefined;
|
||||
|
||||
const getScreenshots = () => {
|
||||
if (app?.screenshots?.length) return app.screenshots;
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ const buildSyntheticRole = (
|
||||
})),
|
||||
permissionFlags: (defaultRole.permissionFlags ?? []).map(
|
||||
(permissionFlag) => ({
|
||||
__typename: 'PermissionFlag' as const,
|
||||
__typename: 'RolePermissionFlag' as const,
|
||||
id: uuidv4(),
|
||||
roleId: defaultRole.universalIdentifier,
|
||||
flag: permissionFlag.flag,
|
||||
|
||||
+3
-1
@@ -32,6 +32,7 @@ import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permi
|
||||
import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab/entities/page-layout-tab.entity';
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
@@ -66,7 +67,7 @@ const WORKSPACE_RELATED_ENTITIES: EntityTarget<ObjectLiteral>[] = [
|
||||
ViewFilterGroupEntity,
|
||||
FieldPermissionEntity,
|
||||
ObjectPermissionEntity,
|
||||
PermissionFlagEntity,
|
||||
RolePermissionFlagEntity,
|
||||
RoleTargetEntity,
|
||||
SearchFieldMetadataEntity,
|
||||
RowLevelPermissionPredicateEntity,
|
||||
@@ -81,6 +82,7 @@ const WORKSPACE_RELATED_ENTITIES: EntityTarget<ObjectLiteral>[] = [
|
||||
// Level 2: Children that depend on core entities
|
||||
FieldMetadataEntity,
|
||||
PageLayoutEntity,
|
||||
PermissionFlagEntity,
|
||||
SkillEntity,
|
||||
LogicFunctionEntity,
|
||||
|
||||
|
||||
+102
@@ -0,0 +1,102 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.6.0', 1778235340020)
|
||||
export class RenamePermissionFlagToRolePermissionFlagFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
// The legacy permissionFlag table stores per-role grants (`roleId` + `flag`).
|
||||
// Rename it to preserve those rows before creating the new permissionFlag catalog table.
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."permissionFlag" RENAME TO "rolePermissionFlag"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "PK_a02789db60620a1e9f90147b50f" TO "PK_76591adc8035c2e7b0cd6115136"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "IDX_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE" TO "IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER INDEX "core"."IDX_PERMISSION_FLAG_ROLE_ID" RENAME TO "IDX_ROLE_PERMISSION_FLAG_ROLE_ID"`,
|
||||
);
|
||||
|
||||
// Re-hash inherited constraints/indexes so TypeORM's schema diff matches
|
||||
// the renamed table. Original names were derived from "permissionFlag"
|
||||
// and stay free for the new catalog table created by the next migration.
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_13f8ca9c517976733a1ce4c10eb"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_835bc9f7ef959debfc5cd268049"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_b26a9d39a88d0e72373c677c6c5"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_da8ffd3c24b4a819430a861067"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "IDX_e4559ae0dba56e53714137c704" ON "core"."rolePermissionFlag" ("workspaceId", "universalIdentifier")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_d47b1ebee75d98daa0c870c26e3" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_3835ecc1019327566d35728c8ba" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_4c6ea38698de230b0ec18fa2110" FOREIGN KEY ("roleId") REFERENCES "core"."role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_4c6ea38698de230b0ec18fa2110"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_3835ecc1019327566d35728c8ba"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" DROP CONSTRAINT "FK_d47b1ebee75d98daa0c870c26e3"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX "core"."IDX_e4559ae0dba56e53714137c704"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "IDX_da8ffd3c24b4a819430a861067" ON "core"."rolePermissionFlag" ("workspaceId", "universalIdentifier")`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_b26a9d39a88d0e72373c677c6c5" FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_835bc9f7ef959debfc5cd268049" FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" ADD CONSTRAINT "FK_13f8ca9c517976733a1ce4c10eb" FOREIGN KEY ("roleId") REFERENCES "core"."role"("id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER INDEX "core"."IDX_ROLE_PERMISSION_FLAG_ROLE_ID" RENAME TO "IDX_PERMISSION_FLAG_ROLE_ID"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE" TO "IDX_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" RENAME CONSTRAINT "PK_76591adc8035c2e7b0cd6115136" TO "PK_a02789db60620a1e9f90147b50f"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag" RENAME TO "permissionFlag"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.6.0', 1778235340021)
|
||||
export class PermissionFlagSyncableEntityFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`CREATE TABLE "core"."permissionFlag" (
|
||||
"id" uuid NOT NULL DEFAULT uuid_generate_v4(),
|
||||
"workspaceId" uuid NOT NULL,
|
||||
"applicationId" uuid NOT NULL,
|
||||
"universalIdentifier" uuid NOT NULL,
|
||||
"key" varchar NOT NULL,
|
||||
"label" varchar NOT NULL,
|
||||
"description" text,
|
||||
"icon" varchar,
|
||||
"permissionType" varchar NOT NULL,
|
||||
"createdAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
"updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT now(),
|
||||
CONSTRAINT "IDX_PERMISSION_FLAG_KEY_WORKSPACE_ID_UNIQUE" UNIQUE ("key", "workspaceId"),
|
||||
CONSTRAINT "PK_a02789db60620a1e9f90147b50f" PRIMARY KEY ("id")
|
||||
)`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_PERMISSION_FLAG_APPLICATION_ID" ON "core"."permissionFlag" ("applicationId")`,
|
||||
);
|
||||
|
||||
// Reuses canonical hash names freed by the rename migration so TypeORM's
|
||||
// schema diff matches without further intervention.
|
||||
await queryRunner.query(
|
||||
`CREATE UNIQUE INDEX "IDX_da8ffd3c24b4a819430a861067" ON "core"."permissionFlag" ("workspaceId", "universalIdentifier")`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."permissionFlag"
|
||||
ADD CONSTRAINT "FK_835bc9f7ef959debfc5cd268049"
|
||||
FOREIGN KEY ("workspaceId") REFERENCES "core"."workspace"("id")
|
||||
ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."permissionFlag"
|
||||
ADD CONSTRAINT "FK_b26a9d39a88d0e72373c677c6c5"
|
||||
FOREIGN KEY ("applicationId") REFERENCES "core"."application"("id")
|
||||
ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."permissionFlag" DROP CONSTRAINT IF EXISTS "FK_b26a9d39a88d0e72373c677c6c5"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."permissionFlag" DROP CONSTRAINT IF EXISTS "FK_835bc9f7ef959debfc5cd268049"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."IDX_da8ffd3c24b4a819430a861067"`,
|
||||
);
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."IDX_PERMISSION_FLAG_APPLICATION_ID"`,
|
||||
);
|
||||
await queryRunner.query(`DROP TABLE IF EXISTS "core"."permissionFlag"`);
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
import { QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { FastInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/fast-instance-command.interface';
|
||||
|
||||
@RegisteredInstanceCommand('2.6.0', 1778235340022)
|
||||
export class LinkRolePermissionFlagToPermissionFlagFastInstanceCommand
|
||||
implements FastInstanceCommand
|
||||
{
|
||||
public async up(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
ADD COLUMN IF NOT EXISTS "permissionFlagId" uuid`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
ADD CONSTRAINT "IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID_ROLE_ID_UNIQUE"
|
||||
UNIQUE ("permissionFlagId", "roleId")`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`CREATE INDEX "IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID"
|
||||
ON "core"."rolePermissionFlag" ("permissionFlagId")`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
ADD CONSTRAINT "FK_8724e63323f1331591a3e91b0b3"
|
||||
FOREIGN KEY ("permissionFlagId") REFERENCES "core"."permissionFlag"("id")
|
||||
ON DELETE CASCADE ON UPDATE NO ACTION`,
|
||||
);
|
||||
}
|
||||
|
||||
public async down(queryRunner: QueryRunner): Promise<void> {
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
DROP CONSTRAINT IF EXISTS "FK_8724e63323f1331591a3e91b0b3"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`DROP INDEX IF EXISTS "core"."IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
DROP CONSTRAINT IF EXISTS "IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID_ROLE_ID_UNIQUE"`,
|
||||
);
|
||||
|
||||
await queryRunner.query(
|
||||
`ALTER TABLE "core"."rolePermissionFlag"
|
||||
DROP COLUMN IF EXISTS "permissionFlagId"`,
|
||||
);
|
||||
}
|
||||
}
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
import { TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER } from 'twenty-shared/application';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { DataSource, QueryRunner } from 'typeorm';
|
||||
|
||||
import { RegisteredInstanceCommand } from 'src/engine/core-modules/upgrade/decorators/registered-instance-command.decorator';
|
||||
import { SlowInstanceCommand } from 'src/engine/core-modules/upgrade/interfaces/slow-instance-command.interface';
|
||||
import { STANDARD_PERMISSION_FLAG_DEFINITIONS } from 'src/engine/metadata-modules/permission-flag/constants/standard-permission-flag-definitions.constant';
|
||||
|
||||
const PERMISSION_FLAG_TYPES = Object.values(PermissionFlagType) as string[];
|
||||
|
||||
@RegisteredInstanceCommand('2.6.0', 1778235340023, { type: 'slow' })
|
||||
export class BackfillRolePermissionFlagPermissionFlagIdSlowInstanceCommand
|
||||
implements SlowInstanceCommand
|
||||
{
|
||||
async runDataMigration(dataSource: DataSource): Promise<void> {
|
||||
const unknownFlagRows: { flag: string }[] = await dataSource.query(
|
||||
`SELECT DISTINCT "flag" FROM "core"."rolePermissionFlag"
|
||||
WHERE "flag" <> ALL($1::varchar[])`,
|
||||
[PERMISSION_FLAG_TYPES],
|
||||
);
|
||||
|
||||
if (unknownFlagRows.length > 0) {
|
||||
const unknownFlags = unknownFlagRows.map((row) => row.flag).join(', ');
|
||||
|
||||
throw new Error(
|
||||
`Cannot migrate: rolePermissionFlag rows reference unknown flag value(s): ${unknownFlags}`,
|
||||
);
|
||||
}
|
||||
|
||||
for (const definition of STANDARD_PERMISSION_FLAG_DEFINITIONS) {
|
||||
await dataSource.query(
|
||||
`INSERT INTO "core"."permissionFlag" (
|
||||
"id",
|
||||
"workspaceId",
|
||||
"applicationId",
|
||||
"universalIdentifier",
|
||||
"key",
|
||||
"label",
|
||||
"description",
|
||||
"icon",
|
||||
"permissionType",
|
||||
"createdAt",
|
||||
"updatedAt"
|
||||
)
|
||||
SELECT
|
||||
uuid_generate_v4(),
|
||||
workspace."id",
|
||||
standardApplication."id",
|
||||
$1::uuid,
|
||||
$2,
|
||||
$3,
|
||||
$4,
|
||||
$5,
|
||||
$6,
|
||||
now(),
|
||||
now()
|
||||
FROM "core"."workspace" workspace
|
||||
INNER JOIN "core"."application" standardApplication
|
||||
ON standardApplication."workspaceId" = workspace."id"
|
||||
AND standardApplication."universalIdentifier" = $7
|
||||
AND standardApplication."deletedAt" IS NULL
|
||||
ON CONFLICT ("key", "workspaceId") DO NOTHING`,
|
||||
[
|
||||
definition.universalIdentifier,
|
||||
definition.key,
|
||||
definition.label,
|
||||
definition.description,
|
||||
definition.icon,
|
||||
definition.permissionType,
|
||||
TWENTY_STANDARD_APPLICATION_UNIVERSAL_IDENTIFIER,
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
await dataSource.query(
|
||||
`UPDATE "core"."rolePermissionFlag" rolePermissionFlag
|
||||
SET "permissionFlagId" = permissionFlag."id"
|
||||
FROM "core"."permissionFlag" permissionFlag
|
||||
WHERE permissionFlag."workspaceId" = rolePermissionFlag."workspaceId"
|
||||
AND permissionFlag."key" = rolePermissionFlag."flag"
|
||||
AND rolePermissionFlag."permissionFlagId" IS NULL`,
|
||||
);
|
||||
}
|
||||
|
||||
public async up(_queryRunner: QueryRunner): Promise<void> {}
|
||||
|
||||
public async down(_queryRunner: QueryRunner): Promise<void> {}
|
||||
}
|
||||
+8
@@ -31,6 +31,10 @@ import { AddToolAndWorkflowActionTriggerSettingsFastInstanceCommand } from 'src/
|
||||
import { BackfillApplicationVariableUniversalIdentifierSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-slow-1777966965588-backfill-application-variable-universal-identifier';
|
||||
import { MigrateToolTriggerSettingsSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-3/2-3-instance-command-slow-1797000002000-migrate-tool-trigger-settings';
|
||||
import { AddMetadataToBillingPriceFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-4/2-4-instance-command-fast-1777100000000-add-metadata-to-billing-price';
|
||||
import { RenamePermissionFlagToRolePermissionFlagFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340020-rename-permission-flag-to-role-permission-flag';
|
||||
import { PermissionFlagSyncableEntityFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340021-permission-flag-syncable-entity';
|
||||
import { LinkRolePermissionFlagToPermissionFlagFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-fast-1778235340022-link-role-permission-flag-to-permission-flag';
|
||||
import { BackfillRolePermissionFlagPermissionFlagIdSlowInstanceCommand } from 'src/database/commands/upgrade-version-command/2-6/2-6-instance-command-slow-1778235340023-backfill-role-permission-flag-permission-flag-id';
|
||||
import { AddEmailGroupChannelTypeFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-4/2-4-instance-command-fast-1778256809018-add-email-group-channel-type';
|
||||
import { AddApplicationIdToPublicDomainFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-4/2-4-instance-command-fast-1798000003000-add-application-id-to-public-domain';
|
||||
import { AddIsInternalMessagesImportEnabledFastInstanceCommand } from 'src/database/commands/upgrade-version-command/2-5/2-5-instance-command-fast-1778525104406-add-is-internal-messages-import-enabled';
|
||||
@@ -77,6 +81,10 @@ export const INSTANCE_COMMANDS = [
|
||||
RemoveUserDefaultAvatarUrlFastInstanceCommand,
|
||||
TransformApplicationVariableToSyncableEntityFastInstanceCommand,
|
||||
BackfillApplicationVariableUniversalIdentifierSlowInstanceCommand,
|
||||
RenamePermissionFlagToRolePermissionFlagFastInstanceCommand,
|
||||
PermissionFlagSyncableEntityFastInstanceCommand,
|
||||
LinkRolePermissionFlagToPermissionFlagFastInstanceCommand,
|
||||
BackfillRolePermissionFlagPermissionFlagIdSlowInstanceCommand,
|
||||
AddEmailGroupChannelTypeFastInstanceCommand,
|
||||
AddApplicationIdToPublicDomainFastInstanceCommand,
|
||||
AddIsInternalMessagesImportEnabledFastInstanceCommand,
|
||||
|
||||
-23
@@ -1,23 +0,0 @@
|
||||
import { type UniversalFlatPermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-permission-flag.type';
|
||||
import { PermissionFlagManifest } from 'twenty-shared/application';
|
||||
|
||||
export const fromPermissionFlagToUniversalFlatPermissionFlag = ({
|
||||
permissionFlag,
|
||||
roleUniversalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}: {
|
||||
permissionFlag: PermissionFlagManifest;
|
||||
roleUniversalIdentifier: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
now: string;
|
||||
}): UniversalFlatPermissionFlag => {
|
||||
return {
|
||||
universalIdentifier: permissionFlag.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
flag: permissionFlag.flag,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
};
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
import { type PermissionFlagManifest } from 'twenty-shared/application';
|
||||
import { SystemPermissionFlag } from 'twenty-shared/constants';
|
||||
|
||||
import { type UniversalFlatRolePermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role-permission-flag.type';
|
||||
|
||||
export const fromPermissionFlagToUniversalFlatRolePermissionFlag = ({
|
||||
permissionFlag,
|
||||
roleUniversalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}: {
|
||||
permissionFlag: PermissionFlagManifest;
|
||||
roleUniversalIdentifier: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
now: string;
|
||||
}): UniversalFlatRolePermissionFlag => {
|
||||
return {
|
||||
universalIdentifier: permissionFlag.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
permissionFlagUniversalIdentifier:
|
||||
SystemPermissionFlag[permissionFlag.flag],
|
||||
flag: permissionFlag.flag,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
};
|
||||
+1
-1
@@ -32,7 +32,7 @@ export const fromRoleManifestToUniversalFlatRole = ({
|
||||
roleTargetUniversalIdentifiers: [],
|
||||
objectPermissionUniversalIdentifiers: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
permissionFlagUniversalIdentifiers: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
rowLevelPermissionPredicateUniversalIdentifiers: [],
|
||||
rowLevelPermissionPredicateGroupUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
|
||||
+9
-8
@@ -17,7 +17,7 @@ import { fromObjectPermissionManifestToUniversalFlatObjectPermission } from 'src
|
||||
import { fromPageLayoutManifestToUniversalFlatPageLayout } from 'src/engine/core-modules/application/application-manifest/converters/from-page-layout-manifest-to-universal-flat-page-layout.util';
|
||||
import { fromPageLayoutTabManifestToUniversalFlatPageLayoutTab } from 'src/engine/core-modules/application/application-manifest/converters/from-page-layout-tab-manifest-to-universal-flat-page-layout-tab.util';
|
||||
import { fromPageLayoutWidgetManifestToUniversalFlatPageLayoutWidget } from 'src/engine/core-modules/application/application-manifest/converters/from-page-layout-widget-manifest-to-universal-flat-page-layout-widget.util';
|
||||
import { fromPermissionFlagToUniversalFlatPermissionFlag } from 'src/engine/core-modules/application/application-manifest/converters/from-permission-flag-to-universal-flat-permission-flag.util';
|
||||
import { fromPermissionFlagToUniversalFlatRolePermissionFlag } from 'src/engine/core-modules/application/application-manifest/converters/from-permission-flag-to-universal-flat-role-permission-flag.util';
|
||||
import { fromRoleManifestToUniversalFlatRole } from 'src/engine/core-modules/application/application-manifest/converters/from-role-manifest-to-universal-flat-role.util';
|
||||
import { fromSkillManifestToUniversalFlatSkill } from 'src/engine/core-modules/application/application-manifest/converters/from-skill-manifest-to-universal-flat-skill.util';
|
||||
import { computeSearchVectorUniversalSettingsFromObjectManifest } from 'src/engine/core-modules/application/application-manifest/utils/compute-search-vector-universal-settings-from-object-manifest.util';
|
||||
@@ -214,14 +214,15 @@ export const computeApplicationManifestAllUniversalFlatEntityMaps = ({
|
||||
|
||||
for (const permissionFlag of roleManifest.permissionFlags ?? []) {
|
||||
addUniversalFlatEntityToUniversalFlatEntityMapsThroughMutationOrThrow({
|
||||
universalFlatEntity: fromPermissionFlagToUniversalFlatPermissionFlag({
|
||||
permissionFlag,
|
||||
roleUniversalIdentifier: roleManifest.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}),
|
||||
universalFlatEntity:
|
||||
fromPermissionFlagToUniversalFlatRolePermissionFlag({
|
||||
permissionFlag,
|
||||
roleUniversalIdentifier: roleManifest.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
now,
|
||||
}),
|
||||
universalFlatEntityMapsToMutate:
|
||||
allUniversalFlatEntityMaps.flatPermissionFlagMaps,
|
||||
allUniversalFlatEntityMaps.flatRolePermissionFlagMaps,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+13
-2
@@ -229,8 +229,11 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
},
|
||||
"permissionFlag": {
|
||||
"propertiesToCompare": [
|
||||
"flag",
|
||||
"roleUniversalIdentifier",
|
||||
"key",
|
||||
"label",
|
||||
"description",
|
||||
"icon",
|
||||
"permissionType",
|
||||
],
|
||||
"propertiesToStringify": [],
|
||||
},
|
||||
@@ -251,6 +254,14 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_PROPERTIES_TO_COMPARE_AND_STRINGIFY should ma
|
||||
],
|
||||
"propertiesToStringify": [],
|
||||
},
|
||||
"rolePermissionFlag": {
|
||||
"propertiesToCompare": [
|
||||
"permissionFlagUniversalIdentifier",
|
||||
"roleUniversalIdentifier",
|
||||
"flag",
|
||||
],
|
||||
"propertiesToStringify": [],
|
||||
},
|
||||
"roleTarget": {
|
||||
"propertiesToCompare": [
|
||||
"roleUniversalIdentifier",
|
||||
|
||||
+45
-3
@@ -1186,17 +1186,59 @@ export const ALL_ENTITY_PROPERTIES_CONFIGURATION_BY_METADATA_NAME = {
|
||||
universalProperty: 'pageLayoutUniversalIdentifier',
|
||||
},
|
||||
},
|
||||
permissionFlag: {
|
||||
flag: {
|
||||
rolePermissionFlag: {
|
||||
permissionFlagId: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
universalProperty: 'permissionFlagUniversalIdentifier',
|
||||
},
|
||||
roleId: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: 'roleUniversalIdentifier',
|
||||
},
|
||||
flag: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
createdAt: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
updatedAt: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
},
|
||||
permissionFlag: {
|
||||
key: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
label: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
description: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
icon: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
permissionType: {
|
||||
toCompare: true,
|
||||
toStringify: false,
|
||||
universalProperty: undefined,
|
||||
},
|
||||
createdAt: {
|
||||
toCompare: false,
|
||||
toStringify: false,
|
||||
|
||||
+8
-1
@@ -171,12 +171,19 @@ export const ALL_MANY_TO_ONE_METADATA_FOREIGN_KEY = {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
permissionFlag: {
|
||||
rolePermissionFlag: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
role: {
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
permissionFlag: {
|
||||
foreignKey: 'permissionFlagId',
|
||||
},
|
||||
},
|
||||
permissionFlag: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
objectPermission: {
|
||||
workspace: null,
|
||||
|
||||
+13
-2
@@ -300,16 +300,27 @@ export const ALL_MANY_TO_ONE_METADATA_RELATIONS = {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
permissionFlag: {
|
||||
rolePermissionFlag: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
role: {
|
||||
metadataName: 'role',
|
||||
foreignKey: 'roleId',
|
||||
inverseOneToManyProperty: 'permissionFlags',
|
||||
inverseOneToManyProperty: 'rolePermissionFlags',
|
||||
isNullable: false,
|
||||
universalForeignKey: 'roleUniversalIdentifier',
|
||||
},
|
||||
permissionFlag: {
|
||||
metadataName: 'permissionFlag',
|
||||
foreignKey: 'permissionFlagId',
|
||||
inverseOneToManyProperty: 'rolePermissionFlags',
|
||||
isNullable: true,
|
||||
universalForeignKey: 'permissionFlagUniversalIdentifier',
|
||||
},
|
||||
},
|
||||
permissionFlag: {
|
||||
workspace: null,
|
||||
application: null,
|
||||
},
|
||||
objectPermission: {
|
||||
workspace: null,
|
||||
|
||||
+2
@@ -17,6 +17,7 @@ import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
@@ -56,6 +57,7 @@ export const ALL_METADATA_ENTITY_BY_METADATA_NAME = {
|
||||
agent: AgentEntity,
|
||||
commandMenuItem: CommandMenuItemEntity,
|
||||
navigationMenuItem: NavigationMenuItemEntity,
|
||||
rolePermissionFlag: RolePermissionFlagEntity,
|
||||
permissionFlag: PermissionFlagEntity,
|
||||
webhook: WebhookEntity,
|
||||
applicationVariable: ApplicationVariableEntity,
|
||||
|
||||
+5
-1
@@ -76,9 +76,13 @@ export const ALL_METADATA_REQUIRED_METADATA_FOR_VALIDATION = {
|
||||
view: true,
|
||||
pageLayout: true,
|
||||
},
|
||||
permissionFlag: {
|
||||
rolePermissionFlag: {
|
||||
permissionFlag: true,
|
||||
role: true,
|
||||
},
|
||||
permissionFlag: {
|
||||
rolePermissionFlag: true,
|
||||
},
|
||||
objectPermission: {
|
||||
role: true,
|
||||
objectMetadata: true,
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ export const ALL_METADATA_SERIALIZED_RELATION = {
|
||||
logicFunction: {},
|
||||
role: {},
|
||||
roleTarget: {},
|
||||
rolePermissionFlag: {},
|
||||
permissionFlag: {},
|
||||
objectPermission: {},
|
||||
fieldPermission: {},
|
||||
|
||||
+13
-5
@@ -168,11 +168,11 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
|
||||
universalFlatEntityForeignKeyAggregator:
|
||||
'objectPermissionUniversalIdentifiers',
|
||||
},
|
||||
permissionFlags: {
|
||||
metadataName: 'permissionFlag',
|
||||
flatEntityForeignKeyAggregator: 'permissionFlagIds',
|
||||
rolePermissionFlags: {
|
||||
metadataName: 'rolePermissionFlag',
|
||||
flatEntityForeignKeyAggregator: 'rolePermissionFlagIds',
|
||||
universalFlatEntityForeignKeyAggregator:
|
||||
'permissionFlagUniversalIdentifiers',
|
||||
'rolePermissionFlagUniversalIdentifiers',
|
||||
},
|
||||
fieldPermissions: {
|
||||
metadataName: 'fieldPermission',
|
||||
@@ -194,7 +194,15 @@ export const ALL_ONE_TO_MANY_METADATA_RELATIONS = {
|
||||
},
|
||||
},
|
||||
roleTarget: {},
|
||||
permissionFlag: {},
|
||||
rolePermissionFlag: {},
|
||||
permissionFlag: {
|
||||
rolePermissionFlags: {
|
||||
metadataName: 'rolePermissionFlag',
|
||||
flatEntityForeignKeyAggregator: 'rolePermissionFlagIds',
|
||||
universalFlatEntityForeignKeyAggregator:
|
||||
'rolePermissionFlagUniversalIdentifiers',
|
||||
},
|
||||
},
|
||||
objectPermission: {},
|
||||
fieldPermission: {},
|
||||
pageLayout: {
|
||||
|
||||
+5
@@ -13,6 +13,7 @@ import { WorkspaceFlatPageLayoutTabMapCacheService } from 'src/engine/metadata-m
|
||||
import { WorkspaceFlatPageLayoutWidgetMapCacheService } from 'src/engine/metadata-modules/flat-page-layout-widget/services/workspace-flat-page-layout-widget-map-cache.service';
|
||||
import { WorkspaceFlatPageLayoutMapCacheService } from 'src/engine/metadata-modules/flat-page-layout/services/workspace-flat-page-layout-map-cache.service';
|
||||
import { WorkspaceFlatPermissionFlagMapCacheService } from 'src/engine/metadata-modules/flat-permission-flag/services/workspace-flat-permission-flag-map-cache.service';
|
||||
import { WorkspaceFlatRolePermissionFlagMapCacheService } from 'src/engine/metadata-modules/flat-role-permission-flag/services/workspace-flat-role-permission-flag-map-cache.service';
|
||||
import { WorkspaceFlatRowLevelPermissionPredicateGroupMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-group-map-cache.service';
|
||||
import { WorkspaceFlatRowLevelPermissionPredicateMapCacheService } from 'src/engine/metadata-modules/flat-row-level-permission-predicate/services/workspace-flat-row-level-permission-predicate-map-cache.service';
|
||||
import { WorkspaceFlatViewFieldGroupMapCacheService } from 'src/engine/metadata-modules/flat-view-field-group/services/workspace-flat-view-field-group-map-cache.service';
|
||||
@@ -32,6 +33,7 @@ import { PageLayoutTabEntity } from 'src/engine/metadata-modules/page-layout-tab
|
||||
import { PageLayoutWidgetEntity } from 'src/engine/metadata-modules/page-layout-widget/entities/page-layout-widget.entity';
|
||||
import { PageLayoutEntity } from 'src/engine/metadata-modules/page-layout/entities/page-layout.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
@@ -64,6 +66,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
PageLayoutEntity,
|
||||
PageLayoutTabEntity,
|
||||
PageLayoutWidgetEntity,
|
||||
RolePermissionFlagEntity,
|
||||
PermissionFlagEntity,
|
||||
RowLevelPermissionPredicateEntity,
|
||||
RowLevelPermissionPredicateGroupEntity,
|
||||
@@ -85,6 +88,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
WorkspaceFlatViewGroupMapCacheService,
|
||||
WorkspaceFlatObjectPermissionMapCacheService,
|
||||
WorkspaceFlatFieldPermissionMapCacheService,
|
||||
WorkspaceFlatRolePermissionFlagMapCacheService,
|
||||
WorkspaceFlatPermissionFlagMapCacheService,
|
||||
WorkspaceFlatViewSortMapCacheService,
|
||||
WorkspaceFlatPageLayoutMapCacheService,
|
||||
@@ -106,6 +110,7 @@ import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache
|
||||
WorkspaceFlatViewGroupMapCacheService,
|
||||
WorkspaceFlatObjectPermissionMapCacheService,
|
||||
WorkspaceFlatFieldPermissionMapCacheService,
|
||||
WorkspaceFlatRolePermissionFlagMapCacheService,
|
||||
WorkspaceFlatPermissionFlagMapCacheService,
|
||||
WorkspaceFlatViewSortMapCacheService,
|
||||
WorkspaceFlatPageLayoutMapCacheService,
|
||||
|
||||
+27
@@ -26,6 +26,8 @@ import { type FlatPageLayoutMaps } from 'src/engine/metadata-modules/flat-page-l
|
||||
import { type FlatPageLayout } from 'src/engine/metadata-modules/flat-page-layout/types/flat-page-layout.type';
|
||||
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type FlatRolePermissionFlagMaps } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag-maps.type';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { type FlatRoleTargetMaps } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target-maps.type';
|
||||
import { type FlatRoleTarget } from 'src/engine/metadata-modules/flat-role-target/types/flat-role-target.type';
|
||||
import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-role.type';
|
||||
@@ -70,6 +72,7 @@ import { type UniversalFlatPageLayoutTab } from 'src/engine/workspace-manager/wo
|
||||
import { type UniversalFlatPageLayoutWidget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout-widget.type';
|
||||
import { type UniversalFlatPageLayout } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-page-layout.type';
|
||||
import { type UniversalFlatPermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-permission-flag.type';
|
||||
import { type UniversalFlatRolePermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role-permission-flag.type';
|
||||
import { type UniversalFlatRoleTarget } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role-target.type';
|
||||
import { type UniversalFlatRole } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role.type';
|
||||
import { type UniversalFlatRowLevelPermissionPredicateGroup } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-row-level-permission-predicate-group.type';
|
||||
@@ -204,6 +207,14 @@ import {
|
||||
type UniversalDeletePermissionFlagAction,
|
||||
type UniversalUpdatePermissionFlagAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
|
||||
import {
|
||||
type FlatCreateRolePermissionFlagAction,
|
||||
type FlatDeleteRolePermissionFlagAction,
|
||||
type FlatUpdateRolePermissionFlagAction,
|
||||
type UniversalCreateRolePermissionFlagAction,
|
||||
type UniversalDeleteRolePermissionFlagAction,
|
||||
type UniversalUpdateRolePermissionFlagAction,
|
||||
} from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-permission-flag/types/workspace-migration-role-permission-flag-action.type';
|
||||
import {
|
||||
type FlatCreateRoleTargetAction,
|
||||
type FlatDeleteRoleTargetAction,
|
||||
@@ -625,6 +636,22 @@ export type AllFlatEntityTypesByMetadataName = {
|
||||
universalFlatEntity: UniversalFlatNavigationMenuItem;
|
||||
entity: NavigationMenuItemEntity;
|
||||
};
|
||||
rolePermissionFlag: {
|
||||
flatEntityMaps: FlatRolePermissionFlagMaps;
|
||||
universalActions: {
|
||||
create: UniversalCreateRolePermissionFlagAction;
|
||||
update: UniversalUpdateRolePermissionFlagAction;
|
||||
delete: UniversalDeleteRolePermissionFlagAction;
|
||||
};
|
||||
flatActions: {
|
||||
create: FlatCreateRolePermissionFlagAction;
|
||||
update: FlatUpdateRolePermissionFlagAction;
|
||||
delete: FlatDeleteRolePermissionFlagAction;
|
||||
};
|
||||
flatEntity: FlatRolePermissionFlag;
|
||||
universalFlatEntity: UniversalFlatRolePermissionFlag;
|
||||
entity: MetadataEntity<'rolePermissionFlag'>;
|
||||
};
|
||||
permissionFlag: {
|
||||
flatEntityMaps: FlatPermissionFlagMaps;
|
||||
universalActions: {
|
||||
|
||||
+9
-2
@@ -93,7 +93,7 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for permissionFlag 1`] = `
|
||||
[
|
||||
"role",
|
||||
"rolePermissionFlag",
|
||||
]
|
||||
`;
|
||||
|
||||
@@ -101,13 +101,20 @@ exports[`getMetadataRelatedMetadataNames should return related metadata names fo
|
||||
[
|
||||
"roleTarget",
|
||||
"objectPermission",
|
||||
"permissionFlag",
|
||||
"rolePermissionFlag",
|
||||
"fieldPermission",
|
||||
"rowLevelPermissionPredicate",
|
||||
"rowLevelPermissionPredicateGroup",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for rolePermissionFlag 1`] = `
|
||||
[
|
||||
"role",
|
||||
"permissionFlag",
|
||||
]
|
||||
`;
|
||||
|
||||
exports[`getMetadataRelatedMetadataNames should return related metadata names for roleTarget 1`] = `
|
||||
[
|
||||
"role",
|
||||
|
||||
+2
-1
@@ -10,10 +10,10 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
|
||||
"viewField",
|
||||
"objectPermission",
|
||||
"pageLayoutWidget",
|
||||
"rolePermissionFlag",
|
||||
"viewSort",
|
||||
"index",
|
||||
"pageLayout",
|
||||
"permissionFlag",
|
||||
"roleTarget",
|
||||
"rowLevelPermissionPredicateGroup",
|
||||
"viewGroup",
|
||||
@@ -27,6 +27,7 @@ exports[`sortMetadataNamesChildrenFirst should return metadata names sorted with
|
||||
"viewFieldGroup",
|
||||
"viewFilterGroup",
|
||||
"webhook",
|
||||
"permissionFlag",
|
||||
"view",
|
||||
"fieldMetadata",
|
||||
"objectMetadata",
|
||||
|
||||
+4
-2
@@ -1,6 +1,8 @@
|
||||
import { type MetadataEntityPropertyName } from 'src/engine/metadata-modules/flat-entity/constant/all-entity-properties-configuration-by-metadata-name.constant';
|
||||
|
||||
export const FLAT_PERMISSION_FLAG_EDITABLE_PROPERTIES = [
|
||||
'flag',
|
||||
'roleId',
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
'permissionType',
|
||||
] as const satisfies MetadataEntityPropertyName<'permissionFlag'>[];
|
||||
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { TypeOrmModule } from '@nestjs/typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { WorkspaceFlatPermissionFlagMapCacheService } from 'src/engine/metadata-modules/flat-permission-flag/services/workspace-flat-permission-flag-map-cache.service';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forFeature([
|
||||
ApplicationEntity,
|
||||
PermissionFlagEntity,
|
||||
RolePermissionFlagEntity,
|
||||
]),
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [WorkspaceFlatPermissionFlagMapCacheService],
|
||||
exports: [WorkspaceFlatPermissionFlagMapCacheService],
|
||||
})
|
||||
export class FlatPermissionFlagModule {}
|
||||
+34
-27
@@ -8,10 +8,11 @@ import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-enti
|
||||
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
|
||||
import { fromPermissionFlagEntityToFlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-permission-flag-entity-to-flat-permission-flag.util';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
|
||||
import { regroupEntitiesByRelatedEntityId } from 'src/engine/workspace-cache/utils/regroup-entities-by-related-entity-id';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
@Injectable()
|
||||
@@ -22,46 +23,52 @@ export class WorkspaceFlatPermissionFlagMapCacheService extends WorkspaceCachePr
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
@InjectRepository(RoleEntity)
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectRepository(RolePermissionFlagEntity)
|
||||
private readonly rolePermissionFlagRepository: Repository<RolePermissionFlagEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async computeForCache(workspaceId: string): Promise<FlatPermissionFlagMaps> {
|
||||
const [permissionFlags, applications, roles] = await Promise.all([
|
||||
this.permissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.applicationRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.roleRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
const [permissionFlags, applications, rolePermissionFlags] =
|
||||
await Promise.all([
|
||||
this.permissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.applicationRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.rolePermissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const applicationIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(applications);
|
||||
const roleIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(roles);
|
||||
const rolePermissionFlagsByPermissionFlagId =
|
||||
regroupEntitiesByRelatedEntityId<'rolePermissionFlag'>({
|
||||
entities: rolePermissionFlags,
|
||||
foreignKey: 'permissionFlagId',
|
||||
});
|
||||
|
||||
const flatPermissionFlagMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const permissionFlagEntity of permissionFlags) {
|
||||
const flatPermissionFlag = fromPermissionFlagEntityToFlatPermissionFlag({
|
||||
entity: permissionFlagEntity,
|
||||
for (const definition of permissionFlags) {
|
||||
const flatDefinition = fromPermissionFlagEntityToFlatPermissionFlag({
|
||||
entity: {
|
||||
...definition,
|
||||
rolePermissionFlags:
|
||||
rolePermissionFlagsByPermissionFlagId.get(definition.id) ?? [],
|
||||
},
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
roleIdToUniversalIdentifierMap,
|
||||
});
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity: flatPermissionFlag,
|
||||
flatEntity: flatDefinition,
|
||||
flatEntityMapsToMutate: flatPermissionFlagMaps,
|
||||
});
|
||||
}
|
||||
|
||||
+25
-19
@@ -1,38 +1,44 @@
|
||||
import { trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties } from 'twenty-shared/utils';
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type CreatePermissionFlagInput } from 'src/engine/metadata-modules/permission-flag/dtos/create-permission-flag.input';
|
||||
import { type UniversalFlatPermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-permission-flag.type';
|
||||
|
||||
export const fromCreatePermissionFlagInputToFlatPermissionFlagToCreate = ({
|
||||
createPermissionFlagInput,
|
||||
workspaceId,
|
||||
flatApplication,
|
||||
flatRoleMaps,
|
||||
}: {
|
||||
createPermissionFlagInput: CreatePermissionFlagInput;
|
||||
workspaceId: string;
|
||||
flatApplication: FlatApplication;
|
||||
} & Pick<AllFlatEntityMaps, 'flatRoleMaps'>): UniversalFlatPermissionFlag & {
|
||||
id: string;
|
||||
} => {
|
||||
const { roleId, flag, universalIdentifier } = createPermissionFlagInput;
|
||||
}): FlatPermissionFlag => {
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { roleUniversalIdentifier } = resolveEntityRelationUniversalIdentifiers(
|
||||
{
|
||||
metadataName: 'permissionFlag',
|
||||
foreignKeyValues: { roleId },
|
||||
flatEntityMaps: { flatRoleMaps },
|
||||
},
|
||||
);
|
||||
const { key, label, description, icon } =
|
||||
trimAndRemoveDuplicatedWhitespacesFromObjectStringProperties(
|
||||
createPermissionFlagInput,
|
||||
['key', 'label', 'description', 'icon'],
|
||||
);
|
||||
|
||||
const id = createPermissionFlagInput.id ?? v4();
|
||||
const universalIdentifier =
|
||||
createPermissionFlagInput.universalIdentifier ?? id;
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
flag,
|
||||
universalIdentifier: universalIdentifier ?? v4(),
|
||||
id,
|
||||
universalIdentifier,
|
||||
key,
|
||||
label,
|
||||
description: description ?? null,
|
||||
icon: icon ?? null,
|
||||
permissionType: createPermissionFlagInput.permissionType,
|
||||
workspaceId,
|
||||
applicationId: flatApplication.id,
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
rolePermissionFlagIds: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import {
|
||||
PermissionFlagException,
|
||||
PermissionFlagExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permission-flag/permission-flag.exception';
|
||||
|
||||
export const fromDeletePermissionFlagInputToFlatPermissionFlagOrThrow = ({
|
||||
flatPermissionFlagMaps,
|
||||
permissionFlagId,
|
||||
}: {
|
||||
flatPermissionFlagMaps: FlatEntityMaps<FlatPermissionFlag>;
|
||||
permissionFlagId: string;
|
||||
}): FlatPermissionFlag => {
|
||||
const existing = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: permissionFlagId,
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existing)) {
|
||||
throw new PermissionFlagException(
|
||||
'Permission flag not found',
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return existing;
|
||||
};
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
|
||||
|
||||
export const fromFlatPermissionFlagToPermissionFlagDto = (
|
||||
flatDefinition: FlatPermissionFlag,
|
||||
): PermissionFlagDTO => ({
|
||||
id: flatDefinition.id,
|
||||
universalIdentifier: flatDefinition.universalIdentifier,
|
||||
key: flatDefinition.key,
|
||||
label: flatDefinition.label,
|
||||
description: flatDefinition.description,
|
||||
icon: flatDefinition.icon,
|
||||
permissionType: flatDefinition.permissionType,
|
||||
workspaceId: flatDefinition.workspaceId,
|
||||
applicationId: flatDefinition.applicationId,
|
||||
createdAt: new Date(flatDefinition.createdAt),
|
||||
updatedAt: new Date(flatDefinition.updatedAt),
|
||||
});
|
||||
+10
-17
@@ -11,9 +11,8 @@ import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/type
|
||||
export const fromPermissionFlagEntityToFlatPermissionFlag = ({
|
||||
entity: permissionFlagEntity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
roleIdToUniversalIdentifierMap,
|
||||
}: FromEntityToFlatEntityArgs<'permissionFlag'>): FlatPermissionFlag => {
|
||||
const permissionFlagEntityWithoutRelations = removePropertiesFromRecord(
|
||||
const entityWithoutRelations = removePropertiesFromRecord(
|
||||
permissionFlagEntity,
|
||||
getMetadataEntityRelationProperties('permissionFlag'),
|
||||
);
|
||||
@@ -30,24 +29,18 @@ export const fromPermissionFlagEntityToFlatPermissionFlag = ({
|
||||
);
|
||||
}
|
||||
|
||||
const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get(
|
||||
permissionFlagEntity.roleId,
|
||||
);
|
||||
|
||||
if (!isDefined(roleUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`Role with id ${permissionFlagEntity.roleId} not found for permissionFlag ${permissionFlagEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...permissionFlagEntityWithoutRelations,
|
||||
...entityWithoutRelations,
|
||||
createdAt: permissionFlagEntity.createdAt.toISOString(),
|
||||
updatedAt: permissionFlagEntity.updatedAt.toISOString(),
|
||||
universalIdentifier:
|
||||
permissionFlagEntityWithoutRelations.universalIdentifier,
|
||||
universalIdentifier: entityWithoutRelations.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
rolePermissionFlagIds: permissionFlagEntity.rolePermissionFlags.map(
|
||||
({ id }) => id,
|
||||
),
|
||||
rolePermissionFlagUniversalIdentifiers:
|
||||
permissionFlagEntity.rolePermissionFlags.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
};
|
||||
};
|
||||
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { FLAT_PERMISSION_FLAG_EDITABLE_PROPERTIES } from 'src/engine/metadata-modules/flat-permission-flag/constants/flat-permission-flag-editable-properties.constant';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import {
|
||||
PermissionFlagException,
|
||||
PermissionFlagExceptionCode,
|
||||
} from 'src/engine/metadata-modules/permission-flag/permission-flag.exception';
|
||||
import { type UpdatePermissionFlagInput } from 'src/engine/metadata-modules/permission-flag/dtos/update-permission-flag.input';
|
||||
import { mergeUpdateInExistingRecord } from 'src/utils/merge-update-in-existing-record.util';
|
||||
|
||||
export const fromUpdatePermissionFlagInputToFlatPermissionFlagToUpdateOrThrow =
|
||||
({
|
||||
flatPermissionFlagMaps,
|
||||
updatePermissionFlagInput,
|
||||
}: {
|
||||
flatPermissionFlagMaps: FlatEntityMaps<FlatPermissionFlag>;
|
||||
updatePermissionFlagInput: UpdatePermissionFlagInput;
|
||||
}): FlatPermissionFlag => {
|
||||
const existing = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: updatePermissionFlagInput.id,
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existing)) {
|
||||
throw new PermissionFlagException(
|
||||
'Permission flag not found',
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...mergeUpdateInExistingRecord({
|
||||
existing,
|
||||
properties: [...FLAT_PERMISSION_FLAG_EDITABLE_PROPERTIES],
|
||||
update: updatePermissionFlagInput.update,
|
||||
}),
|
||||
updatedAt: new Date().toISOString(),
|
||||
};
|
||||
};
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import {
|
||||
STANDARD_PERMISSION_FLAG_DEFINITIONS,
|
||||
type StandardPermissionFlagDefinition,
|
||||
} from 'src/engine/metadata-modules/permission-flag/constants/standard-permission-flag-definitions.constant';
|
||||
|
||||
const STANDARD_PERMISSION_FLAG_DEFINITION_BY_KEY: Record<
|
||||
PermissionFlagType,
|
||||
StandardPermissionFlagDefinition
|
||||
> = STANDARD_PERMISSION_FLAG_DEFINITIONS.reduce(
|
||||
(accumulator, definition) => {
|
||||
accumulator[definition.key] = definition;
|
||||
|
||||
return accumulator;
|
||||
},
|
||||
{} as Record<PermissionFlagType, StandardPermissionFlagDefinition>,
|
||||
);
|
||||
|
||||
const SYNTHESIZED_TIMESTAMP = new Date(0).toISOString();
|
||||
|
||||
// TODO: Remove this once we have a proper permission flag catalog backfilled
|
||||
export const synthesizeFlatPermissionFlagFromFlag = ({
|
||||
flag,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
}: {
|
||||
flag: PermissionFlagType;
|
||||
workspaceId: string;
|
||||
applicationId: string;
|
||||
applicationUniversalIdentifier: string;
|
||||
}): FlatPermissionFlag => {
|
||||
const definition = STANDARD_PERMISSION_FLAG_DEFINITION_BY_KEY[flag];
|
||||
const universalIdentifier = SystemPermissionFlag[flag];
|
||||
|
||||
return {
|
||||
id: universalIdentifier,
|
||||
universalIdentifier,
|
||||
workspaceId,
|
||||
applicationId,
|
||||
applicationUniversalIdentifier,
|
||||
key: flag,
|
||||
label: definition.label,
|
||||
description: definition.description,
|
||||
icon: definition.icon,
|
||||
permissionType: definition.permissionType,
|
||||
rolePermissionFlagIds: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
createdAt: SYNTHESIZED_TIMESTAMP,
|
||||
updatedAt: SYNTHESIZED_TIMESTAMP,
|
||||
};
|
||||
};
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { Repository } from 'typeorm';
|
||||
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type FlatRolePermissionFlagMaps } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag-maps.type';
|
||||
import { fromRolePermissionFlagEntityToFlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/utils/from-role-permission-flag-entity-to-flat-role-permission-flag.util';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/workspace-cache-provider.service';
|
||||
import { WorkspaceCache } from 'src/engine/workspace-cache/decorators/workspace-cache.decorator';
|
||||
import { createIdToUniversalIdentifierMap } from 'src/engine/workspace-cache/utils/create-id-to-universal-identifier-map.util';
|
||||
import { addFlatEntityToFlatEntityMapsThroughMutationOrThrow } from 'src/engine/workspace-manager/workspace-migration/utils/add-flat-entity-to-flat-entity-maps-through-mutation-or-throw.util';
|
||||
|
||||
@Injectable()
|
||||
@WorkspaceCache('flatRolePermissionFlagMaps')
|
||||
export class WorkspaceFlatRolePermissionFlagMapCacheService extends WorkspaceCacheProvider<FlatRolePermissionFlagMaps> {
|
||||
constructor(
|
||||
@InjectRepository(RolePermissionFlagEntity)
|
||||
private readonly rolePermissionFlagRepository: Repository<RolePermissionFlagEntity>,
|
||||
@InjectRepository(ApplicationEntity)
|
||||
private readonly applicationRepository: Repository<ApplicationEntity>,
|
||||
@InjectRepository(RoleEntity)
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectRepository(PermissionFlagEntity)
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
async computeForCache(
|
||||
workspaceId: string,
|
||||
): Promise<FlatRolePermissionFlagMaps> {
|
||||
const [rolePermissionFlags, applications, roles, permissionFlags] =
|
||||
await Promise.all([
|
||||
this.rolePermissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.applicationRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.roleRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.permissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
]);
|
||||
|
||||
const applicationIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(applications);
|
||||
const roleIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(roles);
|
||||
const permissionFlagIdToUniversalIdentifierMap =
|
||||
createIdToUniversalIdentifierMap(permissionFlags);
|
||||
|
||||
const flatRolePermissionFlagMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const rolePermissionFlagEntity of rolePermissionFlags) {
|
||||
const flatRolePermissionFlag =
|
||||
fromRolePermissionFlagEntityToFlatRolePermissionFlag({
|
||||
entity: rolePermissionFlagEntity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
permissionFlagIdToUniversalIdentifierMap,
|
||||
roleIdToUniversalIdentifierMap,
|
||||
});
|
||||
|
||||
addFlatEntityToFlatEntityMapsThroughMutationOrThrow({
|
||||
flatEntity: flatRolePermissionFlag,
|
||||
flatEntityMapsToMutate: flatRolePermissionFlagMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return flatRolePermissionFlagMaps;
|
||||
}
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
|
||||
export type FlatRolePermissionFlagMaps = FlatEntityMaps<FlatRolePermissionFlag>;
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-from.type';
|
||||
import { type RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
|
||||
export type FlatRolePermissionFlag = FlatEntityFrom<RolePermissionFlagEntity>;
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { type FlatApplication } from 'src/engine/core-modules/application/types/flat-application.type';
|
||||
import { type AllFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/all-flat-entity-maps.type';
|
||||
import { resolveEntityRelationUniversalIdentifiers } from 'src/engine/metadata-modules/flat-entity/utils/resolve-entity-relation-universal-identifiers.util';
|
||||
import { type CreateRolePermissionFlagInput } from 'src/engine/metadata-modules/role-permission-flag/dtos/create-role-permission-flag.input';
|
||||
import { type UniversalFlatRolePermissionFlag } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-role-permission-flag.type';
|
||||
|
||||
export const fromCreateRolePermissionFlagInputToFlatRolePermissionFlagToCreate =
|
||||
({
|
||||
createRolePermissionFlagInput,
|
||||
flatApplication,
|
||||
flatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
}: {
|
||||
createRolePermissionFlagInput: CreateRolePermissionFlagInput;
|
||||
flatApplication: FlatApplication;
|
||||
} & Pick<
|
||||
AllFlatEntityMaps,
|
||||
'flatPermissionFlagMaps' | 'flatRoleMaps'
|
||||
>): UniversalFlatRolePermissionFlag & {
|
||||
id: string;
|
||||
} => {
|
||||
const { roleId, permissionFlagId, flag, universalIdentifier } =
|
||||
createRolePermissionFlagInput;
|
||||
const now = new Date().toISOString();
|
||||
|
||||
const { permissionFlagUniversalIdentifier, roleUniversalIdentifier } =
|
||||
resolveEntityRelationUniversalIdentifiers({
|
||||
metadataName: 'rolePermissionFlag',
|
||||
foreignKeyValues: { permissionFlagId, roleId },
|
||||
flatEntityMaps: { flatPermissionFlagMaps, flatRoleMaps },
|
||||
});
|
||||
|
||||
return {
|
||||
id: v4(),
|
||||
universalIdentifier: universalIdentifier ?? v4(),
|
||||
applicationUniversalIdentifier: flatApplication.universalIdentifier,
|
||||
permissionFlagUniversalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
flag,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
};
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
import { SystemPermissionFlag } from 'twenty-shared/constants';
|
||||
import { isDefined, removePropertiesFromRecord } from 'twenty-shared/utils';
|
||||
|
||||
import {
|
||||
FlatEntityMapsException,
|
||||
FlatEntityMapsExceptionCode,
|
||||
} from 'src/engine/metadata-modules/flat-entity/exceptions/flat-entity-maps.exception';
|
||||
import { getMetadataEntityRelationProperties } from 'src/engine/metadata-modules/flat-entity/utils/get-metadata-entity-relation-properties.util';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { type FromEntityToFlatEntityArgs } from 'src/engine/workspace-cache/types/from-entity-to-flat-entity-args.type';
|
||||
|
||||
export const fromRolePermissionFlagEntityToFlatRolePermissionFlag = ({
|
||||
entity: rolePermissionFlagEntity,
|
||||
applicationIdToUniversalIdentifierMap,
|
||||
permissionFlagIdToUniversalIdentifierMap,
|
||||
roleIdToUniversalIdentifierMap,
|
||||
}: FromEntityToFlatEntityArgs<'rolePermissionFlag'>): FlatRolePermissionFlag => {
|
||||
const rolePermissionFlagEntityWithoutRelations = removePropertiesFromRecord(
|
||||
rolePermissionFlagEntity,
|
||||
getMetadataEntityRelationProperties('rolePermissionFlag'),
|
||||
);
|
||||
|
||||
const applicationUniversalIdentifier =
|
||||
applicationIdToUniversalIdentifierMap.get(
|
||||
rolePermissionFlagEntity.applicationId,
|
||||
);
|
||||
|
||||
if (!isDefined(applicationUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`Application with id ${rolePermissionFlagEntity.applicationId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const roleUniversalIdentifier = roleIdToUniversalIdentifierMap.get(
|
||||
rolePermissionFlagEntity.roleId,
|
||||
);
|
||||
|
||||
if (!isDefined(roleUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`Role with id ${rolePermissionFlagEntity.roleId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
const permissionFlagUniversalIdentifier = isDefined(
|
||||
rolePermissionFlagEntity.permissionFlagId,
|
||||
)
|
||||
? permissionFlagIdToUniversalIdentifierMap.get(
|
||||
rolePermissionFlagEntity.permissionFlagId,
|
||||
)
|
||||
: SystemPermissionFlag[rolePermissionFlagEntity.flag];
|
||||
|
||||
if (!isDefined(permissionFlagUniversalIdentifier)) {
|
||||
throw new FlatEntityMapsException(
|
||||
`PermissionFlag with id ${rolePermissionFlagEntity.permissionFlagId} not found for rolePermissionFlag ${rolePermissionFlagEntity.id}`,
|
||||
FlatEntityMapsExceptionCode.ENTITY_NOT_FOUND,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
...rolePermissionFlagEntityWithoutRelations,
|
||||
createdAt: rolePermissionFlagEntity.createdAt.toISOString(),
|
||||
updatedAt: rolePermissionFlagEntity.updatedAt.toISOString(),
|
||||
universalIdentifier:
|
||||
rolePermissionFlagEntityWithoutRelations.universalIdentifier,
|
||||
applicationUniversalIdentifier,
|
||||
permissionFlagUniversalIdentifier,
|
||||
roleUniversalIdentifier,
|
||||
};
|
||||
};
|
||||
+2
-2
@@ -2,8 +2,8 @@ import { type FlatEntityFrom } from 'src/engine/metadata-modules/flat-entity/typ
|
||||
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
export type FlatRole = FlatEntityFrom<RoleEntity> & {
|
||||
// TODO remove once objectPermission permissionFlag fieldPermission have been migrated to v2
|
||||
// TODO remove once objectPermission rolePermissionFlag fieldPermission have been migrated to v2
|
||||
objectPermissionIds: string[];
|
||||
permissionFlagIds: string[];
|
||||
rolePermissionFlagIds: string[];
|
||||
fieldPermissionIds: string[];
|
||||
};
|
||||
|
||||
+2
-2
@@ -54,9 +54,9 @@ export const fromCreateRoleInputToFlatRoleToCreate = ({
|
||||
roleTargetIds: [],
|
||||
roleTargetUniversalIdentifiers: [],
|
||||
objectPermissionIds: [],
|
||||
permissionFlagIds: [],
|
||||
rolePermissionFlagIds: [],
|
||||
objectPermissionUniversalIdentifiers: [],
|
||||
permissionFlagUniversalIdentifiers: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
fieldPermissionIds: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
rowLevelPermissionPredicateIds: [],
|
||||
|
||||
+4
-4
@@ -13,10 +13,10 @@ import { type RegroupedEntity } from 'src/engine/workspace-cache/utils/regroup-e
|
||||
type FromRoleEntityToFlatRoleArgs = {
|
||||
entity: Omit<
|
||||
EntityWithRegroupedOneToManyRelations<MetadataEntity<'role'>>,
|
||||
'objectPermissions' | 'permissionFlags' | 'fieldPermissions'
|
||||
'objectPermissions' | 'rolePermissionFlags' | 'fieldPermissions'
|
||||
> & {
|
||||
objectPermissions: RegroupedEntity[];
|
||||
permissionFlags: RegroupedEntity[];
|
||||
rolePermissionFlags: RegroupedEntity[];
|
||||
fieldPermissions: RegroupedEntity[];
|
||||
};
|
||||
} & EntityManyToOneIdByUniversalIdentifierMaps<'role'>;
|
||||
@@ -57,7 +57,7 @@ export const fromRoleEntityToFlatRole = ({
|
||||
applicationId: roleEntity.applicationId,
|
||||
roleTargetIds: roleEntity.roleTargets.map(({ id }) => id),
|
||||
objectPermissionIds: roleEntity.objectPermissions.map(({ id }) => id),
|
||||
permissionFlagIds: roleEntity.permissionFlags.map(({ id }) => id),
|
||||
rolePermissionFlagIds: roleEntity.rolePermissionFlags.map(({ id }) => id),
|
||||
fieldPermissionIds: roleEntity.fieldPermissions.map(({ id }) => id),
|
||||
rowLevelPermissionPredicateIds: roleEntity.rowLevelPermissionPredicates.map(
|
||||
({ id }) => id,
|
||||
@@ -71,7 +71,7 @@ export const fromRoleEntityToFlatRole = ({
|
||||
objectPermissionUniversalIdentifiers: roleEntity.objectPermissions.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
permissionFlagUniversalIdentifiers: roleEntity.permissionFlags.map(
|
||||
rolePermissionFlagUniversalIdentifiers: roleEntity.rolePermissionFlags.map(
|
||||
({ universalIdentifier }) => universalIdentifier,
|
||||
),
|
||||
fieldPermissionUniversalIdentifiers: roleEntity.fieldPermissions.map(
|
||||
|
||||
@@ -17,6 +17,7 @@ import { MessageChannelMetadataModule } from 'src/engine/metadata-modules/messag
|
||||
import { MessageFolderMetadataModule } from 'src/engine/metadata-modules/message-folder/message-folder-metadata.module';
|
||||
import { NavigationMenuItemModule } from 'src/engine/metadata-modules/navigation-menu-item/navigation-menu-item.module';
|
||||
import { ObjectMetadataModule } from 'src/engine/metadata-modules/object-metadata/object-metadata.module';
|
||||
import { PermissionFlagModule } from 'src/engine/metadata-modules/permission-flag/permission-flag.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { MinimalMetadataModule } from 'src/engine/metadata-modules/minimal-metadata/minimal-metadata.module';
|
||||
import { RoleModule } from 'src/engine/metadata-modules/role/role.module';
|
||||
@@ -47,6 +48,7 @@ import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/work
|
||||
WorkspaceMetadataVersionModule,
|
||||
RoleModule,
|
||||
PermissionsModule,
|
||||
PermissionFlagModule,
|
||||
RouteTriggerModule,
|
||||
WebhookModule,
|
||||
ConnectedAccountMetadataModule,
|
||||
@@ -75,6 +77,7 @@ import { WorkspaceMetadataVersionModule } from 'src/engine/metadata-modules/work
|
||||
ViewModule,
|
||||
RoleModule,
|
||||
PermissionsModule,
|
||||
PermissionFlagModule,
|
||||
WebhookModule,
|
||||
ConnectedAccountMetadataModule,
|
||||
MessageChannelMetadataModule,
|
||||
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
export const PERMISSION_FLAG_PERMISSION_TYPES = ['settings', 'tool'] as const;
|
||||
|
||||
export type PermissionFlagPermissionType =
|
||||
(typeof PERMISSION_FLAG_PERMISSION_TYPES)[number];
|
||||
+160
@@ -0,0 +1,160 @@
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
|
||||
import { type PermissionFlagPermissionType } from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
import { TOOL_PERMISSION_FLAGS } from 'src/engine/metadata-modules/permissions/constants/tool-permission-flags';
|
||||
|
||||
export type StandardPermissionFlagDefinition = {
|
||||
key: PermissionFlagType;
|
||||
universalIdentifier: string;
|
||||
label: string;
|
||||
description: string | null;
|
||||
icon: string | null;
|
||||
permissionType: PermissionFlagPermissionType;
|
||||
};
|
||||
|
||||
type StandardPermissionFlagMetadata = Pick<
|
||||
StandardPermissionFlagDefinition,
|
||||
'label' | 'description' | 'icon'
|
||||
>;
|
||||
|
||||
const STANDARD_PERMISSION_FLAG_METADATA: Record<
|
||||
PermissionFlagType,
|
||||
StandardPermissionFlagMetadata
|
||||
> = {
|
||||
[PermissionFlagType.API_KEYS_AND_WEBHOOKS]: {
|
||||
label: 'API Keys & Webhooks',
|
||||
description: 'Manage API keys and webhooks',
|
||||
icon: 'IconCode',
|
||||
},
|
||||
[PermissionFlagType.WORKSPACE]: {
|
||||
label: 'Workspace',
|
||||
description: 'Set global workspace preferences',
|
||||
icon: 'IconSettings',
|
||||
},
|
||||
[PermissionFlagType.WORKSPACE_MEMBERS]: {
|
||||
label: 'Users',
|
||||
description: 'Add or remove users',
|
||||
icon: 'IconUsers',
|
||||
},
|
||||
[PermissionFlagType.ROLES]: {
|
||||
label: 'Roles',
|
||||
description: 'Define user roles and access levels',
|
||||
icon: 'IconLockOpen',
|
||||
},
|
||||
[PermissionFlagType.DATA_MODEL]: {
|
||||
label: 'Data Model',
|
||||
description: 'Edit data structure and fields',
|
||||
icon: 'IconHierarchy',
|
||||
},
|
||||
[PermissionFlagType.SECURITY]: {
|
||||
label: 'Security',
|
||||
description: 'Manage security policies',
|
||||
icon: 'IconKey',
|
||||
},
|
||||
[PermissionFlagType.WORKFLOWS]: {
|
||||
label: 'Workflows',
|
||||
description: 'Manage workflows',
|
||||
icon: 'IconSettingsAutomation',
|
||||
},
|
||||
[PermissionFlagType.IMPERSONATE]: {
|
||||
label: 'Impersonate',
|
||||
description: 'Impersonate workspace users',
|
||||
icon: 'IconSpy',
|
||||
},
|
||||
[PermissionFlagType.SSO_BYPASS]: {
|
||||
label: 'SSO Bypass',
|
||||
description: 'Enable bypass options',
|
||||
icon: 'IconShield',
|
||||
},
|
||||
[PermissionFlagType.APPLICATIONS]: {
|
||||
label: 'Applications',
|
||||
description: 'Install and manage applications',
|
||||
icon: 'IconApps',
|
||||
},
|
||||
[PermissionFlagType.MARKETPLACE_APPS]: {
|
||||
label: 'Marketplace Apps',
|
||||
description: 'Browse and install marketplace apps',
|
||||
icon: 'IconShoppingBag',
|
||||
},
|
||||
[PermissionFlagType.LAYOUTS]: {
|
||||
label: 'Layouts',
|
||||
description: 'Customize page layouts and UI structure',
|
||||
icon: 'IconLayoutSidebarRightCollapse',
|
||||
},
|
||||
[PermissionFlagType.BILLING]: {
|
||||
label: 'Billing',
|
||||
description: 'Manage billing and subscriptions',
|
||||
icon: 'IconCreditCard',
|
||||
},
|
||||
[PermissionFlagType.AI_SETTINGS]: {
|
||||
label: 'AI',
|
||||
description: 'Create and configure AI agents',
|
||||
icon: 'IconSparkles',
|
||||
},
|
||||
[PermissionFlagType.AI]: {
|
||||
label: 'Ask AI',
|
||||
description: 'Chat with AI agents and use AI features',
|
||||
icon: 'IconSparkles',
|
||||
},
|
||||
[PermissionFlagType.VIEWS]: {
|
||||
label: 'Manage Views',
|
||||
description: 'Create, edit, and delete workspace views',
|
||||
icon: 'IconTable',
|
||||
},
|
||||
[PermissionFlagType.UPLOAD_FILE]: {
|
||||
label: 'Upload Files',
|
||||
description: 'Allow uploading files and attachments',
|
||||
icon: 'IconFileUpload',
|
||||
},
|
||||
[PermissionFlagType.DOWNLOAD_FILE]: {
|
||||
label: 'Download Files',
|
||||
description: 'Allow downloading files and attachments',
|
||||
icon: 'IconDownload',
|
||||
},
|
||||
[PermissionFlagType.SEND_EMAIL_TOOL]: {
|
||||
label: 'Send Email',
|
||||
description: 'Send emails via connected accounts',
|
||||
icon: 'IconMail',
|
||||
},
|
||||
[PermissionFlagType.HTTP_REQUEST_TOOL]: {
|
||||
label: 'HTTP Request',
|
||||
description: 'Make HTTP requests to external APIs',
|
||||
icon: 'IconApi',
|
||||
},
|
||||
[PermissionFlagType.CODE_INTERPRETER_TOOL]: {
|
||||
label: 'Code Interpreter',
|
||||
description: 'Run code to analyze files and data',
|
||||
icon: 'IconCode',
|
||||
},
|
||||
[PermissionFlagType.IMPORT_CSV]: {
|
||||
label: 'Import CSV',
|
||||
description: 'Allow importing data from CSV files',
|
||||
icon: 'IconFileImport',
|
||||
},
|
||||
[PermissionFlagType.EXPORT_CSV]: {
|
||||
label: 'Export CSV',
|
||||
description: 'Allow exporting data to CSV files',
|
||||
icon: 'IconFileExport',
|
||||
},
|
||||
[PermissionFlagType.CONNECTED_ACCOUNTS]: {
|
||||
label: 'Sync Account',
|
||||
description: 'Sync email and calendar accounts',
|
||||
icon: 'IconAt',
|
||||
},
|
||||
[PermissionFlagType.PROFILE_INFORMATION]: {
|
||||
label: 'Edit Profile',
|
||||
description: 'Edit own profile information',
|
||||
icon: 'IconUser',
|
||||
},
|
||||
};
|
||||
|
||||
export const STANDARD_PERMISSION_FLAG_DEFINITIONS: StandardPermissionFlagDefinition[] =
|
||||
Object.values(PermissionFlagType).map((key) => ({
|
||||
key,
|
||||
universalIdentifier: SystemPermissionFlag[key],
|
||||
...STANDARD_PERMISSION_FLAG_METADATA[key],
|
||||
permissionType: TOOL_PERMISSION_FLAGS.includes(key) ? 'tool' : 'settings',
|
||||
}));
|
||||
+50
-5
@@ -1,7 +1,52 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
export type CreatePermissionFlagInput = {
|
||||
roleId: string;
|
||||
flag: PermissionFlagType;
|
||||
import {
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import {
|
||||
PERMISSION_FLAG_PERMISSION_TYPES,
|
||||
type PermissionFlagPermissionType,
|
||||
} from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
|
||||
@InputType()
|
||||
export class CreatePermissionFlagInput {
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
id?: string;
|
||||
|
||||
@IsUUID()
|
||||
@IsOptional()
|
||||
@Field(() => UUIDScalarType, { nullable: true })
|
||||
universalIdentifier?: string;
|
||||
};
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
key: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsIn(PERMISSION_FLAG_PERMISSION_TYPES)
|
||||
@Field(() => String)
|
||||
permissionType: PermissionFlagPermissionType;
|
||||
}
|
||||
|
||||
+57
-7
@@ -1,17 +1,67 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
import { Field, HideField, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
IsDateString,
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import {
|
||||
PERMISSION_FLAG_PERMISSION_TYPES,
|
||||
type PermissionFlagPermissionType,
|
||||
} from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
|
||||
@ObjectType('PermissionFlag')
|
||||
export class PermissionFlagDTO {
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
roleId: string;
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType)
|
||||
universalIdentifier: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
flag: PermissionFlagType;
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
key: string;
|
||||
|
||||
@IsString()
|
||||
@IsNotEmpty()
|
||||
@Field()
|
||||
label: string;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
description: string | null;
|
||||
|
||||
@IsString()
|
||||
@IsOptional()
|
||||
@Field(() => String, { nullable: true })
|
||||
icon: string | null;
|
||||
|
||||
@IsIn(PERMISSION_FLAG_PERMISSION_TYPES)
|
||||
@Field(() => String)
|
||||
permissionType: PermissionFlagPermissionType;
|
||||
|
||||
@HideField()
|
||||
workspaceId: string;
|
||||
|
||||
@Field(() => UUIDScalarType)
|
||||
applicationId: string;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
createdAt: Date;
|
||||
|
||||
@IsDateString()
|
||||
@Field()
|
||||
updatedAt: Date;
|
||||
}
|
||||
|
||||
+57
@@ -0,0 +1,57 @@
|
||||
import { Field, InputType } from '@nestjs/graphql';
|
||||
|
||||
import { Type } from 'class-transformer';
|
||||
import {
|
||||
IsIn,
|
||||
IsNotEmpty,
|
||||
IsOptional,
|
||||
IsString,
|
||||
IsUUID,
|
||||
ValidateNested,
|
||||
} from 'class-validator';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
import {
|
||||
PERMISSION_FLAG_PERMISSION_TYPES,
|
||||
type PermissionFlagPermissionType,
|
||||
} from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
|
||||
@InputType()
|
||||
export class UpdatePermissionFlagInputUpdates {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
label?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
description?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Field({ nullable: true })
|
||||
icon?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsIn(PERMISSION_FLAG_PERMISSION_TYPES)
|
||||
@Field(() => String, { nullable: true })
|
||||
permissionType?: PermissionFlagPermissionType;
|
||||
}
|
||||
|
||||
@InputType()
|
||||
export class UpdatePermissionFlagInput {
|
||||
@IsUUID()
|
||||
@IsNotEmpty()
|
||||
@Field(() => UUIDScalarType, {
|
||||
description: 'The id of the permission flag definition to update',
|
||||
})
|
||||
id: string;
|
||||
|
||||
@Type(() => UpdatePermissionFlagInputUpdates)
|
||||
@ValidateNested()
|
||||
@Field(() => UpdatePermissionFlagInputUpdates, {
|
||||
description: 'The fields to update',
|
||||
})
|
||||
update: UpdatePermissionFlagInputUpdates;
|
||||
}
|
||||
+23
-15
@@ -1,38 +1,46 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
OneToMany,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { type PermissionFlagPermissionType } from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
@Entity('permissionFlag')
|
||||
@Unique('IDX_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE', ['flag', 'roleId'])
|
||||
@Index('IDX_PERMISSION_FLAG_ROLE_ID', ['roleId'])
|
||||
@Unique('IDX_PERMISSION_FLAG_KEY_WORKSPACE_ID_UNIQUE', ['key', 'workspaceId'])
|
||||
@Index('IDX_PERMISSION_FLAG_APPLICATION_ID', ['applicationId'])
|
||||
export class PermissionFlagEntity extends SyncableEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
roleId: string;
|
||||
|
||||
@ManyToOne(() => RoleEntity, (role) => role.permissionFlags, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'roleId' })
|
||||
role: Relation<RoleEntity>;
|
||||
@Column({ nullable: false, type: 'varchar' })
|
||||
key: string;
|
||||
|
||||
@Column({ nullable: false, type: 'varchar' })
|
||||
flag: PermissionFlagType;
|
||||
label: string;
|
||||
|
||||
@Column({ nullable: true, type: 'text' })
|
||||
description: string | null;
|
||||
|
||||
@Column({ nullable: true, type: 'varchar' })
|
||||
icon: string | null;
|
||||
|
||||
@Column({ nullable: false, type: 'varchar' })
|
||||
permissionType: PermissionFlagPermissionType;
|
||||
|
||||
@OneToMany(
|
||||
() => RolePermissionFlagEntity,
|
||||
(rolePermissionFlag) => rolePermissionFlag.permissionFlag,
|
||||
)
|
||||
rolePermissionFlags: Relation<RolePermissionFlagEntity[]>;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { type MessageDescriptor } from '@lingui/core';
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { assertUnreachable } from 'twenty-shared/utils';
|
||||
|
||||
import { CustomException } from 'src/utils/custom-exception';
|
||||
|
||||
export enum PermissionFlagExceptionCode {
|
||||
PERMISSION_FLAG_NOT_FOUND = 'PERMISSION_FLAG_NOT_FOUND',
|
||||
PERMISSION_FLAG_ALREADY_EXISTS = 'PERMISSION_FLAG_ALREADY_EXISTS',
|
||||
INVALID_PERMISSION_FLAG_KEY = 'INVALID_PERMISSION_FLAG_KEY',
|
||||
INVALID_PERMISSION_FLAG_PERMISSION_TYPE = 'INVALID_PERMISSION_FLAG_PERMISSION_TYPE',
|
||||
PERMISSION_FLAG_KEY_IMMUTABLE = 'PERMISSION_FLAG_KEY_IMMUTABLE',
|
||||
PERMISSION_FLAG_IS_STANDARD = 'PERMISSION_FLAG_IS_STANDARD',
|
||||
PERMISSION_FLAG_IN_USE = 'PERMISSION_FLAG_IN_USE',
|
||||
}
|
||||
|
||||
const getPermissionFlagExceptionUserFriendlyMessage = (
|
||||
code: PermissionFlagExceptionCode,
|
||||
) => {
|
||||
switch (code) {
|
||||
case PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND:
|
||||
return msg`Permission flag not found.`;
|
||||
case PermissionFlagExceptionCode.PERMISSION_FLAG_ALREADY_EXISTS:
|
||||
return msg`Permission flag already exists.`;
|
||||
case PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_KEY:
|
||||
return msg`Invalid permission flag key.`;
|
||||
case PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_PERMISSION_TYPE:
|
||||
return msg`Invalid permission flag permission type.`;
|
||||
case PermissionFlagExceptionCode.PERMISSION_FLAG_KEY_IMMUTABLE:
|
||||
return msg`Permission flag key cannot be changed.`;
|
||||
case PermissionFlagExceptionCode.PERMISSION_FLAG_IS_STANDARD:
|
||||
return msg`Standard permission flags cannot be modified.`;
|
||||
case PermissionFlagExceptionCode.PERMISSION_FLAG_IN_USE:
|
||||
return msg`Permission flag is still assigned to a role.`;
|
||||
default:
|
||||
assertUnreachable(code);
|
||||
}
|
||||
};
|
||||
|
||||
export class PermissionFlagException extends CustomException<PermissionFlagExceptionCode> {
|
||||
constructor(
|
||||
message: string,
|
||||
code: PermissionFlagExceptionCode,
|
||||
{ userFriendlyMessage }: { userFriendlyMessage?: MessageDescriptor } = {},
|
||||
) {
|
||||
super(message, code, {
|
||||
userFriendlyMessage:
|
||||
userFriendlyMessage ??
|
||||
getPermissionFlagExceptionUserFriendlyMessage(code),
|
||||
});
|
||||
}
|
||||
}
|
||||
+8
-4
@@ -2,16 +2,20 @@ import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { FlatPermissionFlagModule } from 'src/engine/metadata-modules/flat-permission-flag/flat-permission-flag.module';
|
||||
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { WorkspaceCacheStorageModule } from 'src/engine/workspace-cache-storage/workspace-cache-storage.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceCacheModule,
|
||||
ApplicationModule,
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceCacheStorageModule,
|
||||
ApplicationModule,
|
||||
FlatPermissionFlagModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
providers: [PermissionFlagService],
|
||||
exports: [PermissionFlagService],
|
||||
|
||||
+200
-125
@@ -1,19 +1,18 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { findFlatEntityByIdInFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps-or-throw.util';
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { fromCreatePermissionFlagInputToFlatPermissionFlagToCreate } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-create-permission-flag-input-to-flat-permission-flag-to-create.util';
|
||||
import { type UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { fromDeletePermissionFlagInputToFlatPermissionFlagOrThrow } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-delete-permission-flag-input-to-flat-permission-flag-or-throw.util';
|
||||
import { fromFlatPermissionFlagToPermissionFlagDto } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-flat-permission-flag-to-permission-flag-dto.util';
|
||||
import { fromUpdatePermissionFlagInputToFlatPermissionFlagToUpdateOrThrow } from 'src/engine/metadata-modules/flat-permission-flag/utils/from-update-permission-flag-input-to-flat-permission-flag-to-update-or-throw.util';
|
||||
import { type CreatePermissionFlagInput } from 'src/engine/metadata-modules/permission-flag/dtos/create-permission-flag.input';
|
||||
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
|
||||
import { type UpdatePermissionFlagInput } from 'src/engine/metadata-modules/permission-flag/dtos/update-permission-flag.input';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@@ -25,115 +24,8 @@ export class PermissionFlagService {
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
public async upsertPermissionFlags({
|
||||
workspaceId,
|
||||
input,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
input: UpsertPermissionFlagsInput;
|
||||
}): Promise<FlatPermissionFlag[]> {
|
||||
const { flatPermissionFlagMaps, flatRoleMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps', 'flatRoleMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const roleUniversalId = flatRoleMaps.universalIdentifierById[input.roleId];
|
||||
const role = isDefined(roleUniversalId)
|
||||
? flatRoleMaps.byUniversalIdentifier[roleUniversalId]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(role)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_NOT_FOUND,
|
||||
PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
{
|
||||
userFriendlyMessage: msg`The role you are trying to modify could not be found.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const invalidFlags = input.permissionFlagKeys.filter(
|
||||
(flag) => !Object.values(PermissionFlagType).includes(flag),
|
||||
);
|
||||
|
||||
if (invalidFlags.length > 0) {
|
||||
throw new PermissionsException(
|
||||
`${PermissionsExceptionMessage.INVALID_SETTING}: ${invalidFlags.join(', ')}`,
|
||||
PermissionsExceptionCode.INVALID_SETTING,
|
||||
{
|
||||
userFriendlyMessage: msg`Some of the permissions you selected are not valid. Please try again with valid permission settings.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const roleUniversalIdentifier = role.universalIdentifier;
|
||||
|
||||
const currentPermissionFlagsForRole = Object.values(
|
||||
flatPermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf): pf is FlatPermissionFlag =>
|
||||
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
|
||||
);
|
||||
|
||||
const inputSet = new Set(input.permissionFlagKeys);
|
||||
const existingSet = new Set(
|
||||
currentPermissionFlagsForRole.map((pf) => pf.flag),
|
||||
);
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const flatEntityToCreate = input.permissionFlagKeys
|
||||
.filter((flag) => !existingSet.has(flag))
|
||||
.map((flag) =>
|
||||
fromCreatePermissionFlagInputToFlatPermissionFlagToCreate({
|
||||
createPermissionFlagInput: { roleId: input.roleId, flag },
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
flatRoleMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
const flatEntityToDelete = currentPermissionFlagsForRole.filter(
|
||||
(pf) => !inputSet.has(pf.flag),
|
||||
);
|
||||
|
||||
if (flatEntityToCreate.length === 0 && flatEntityToDelete.length === 0) {
|
||||
const unchanged = currentPermissionFlagsForRole.filter((pf) =>
|
||||
inputSet.has(pf.flag),
|
||||
);
|
||||
return unchanged;
|
||||
}
|
||||
|
||||
const buildAndRunResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
permissionFlag: {
|
||||
flatEntityToCreate,
|
||||
flatEntityToDelete,
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (buildAndRunResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
buildAndRunResult,
|
||||
'Validation errors occurred while upserting permission flags',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatPermissionFlagMaps: freshFlatPermissionFlagMaps } =
|
||||
async findAll(workspaceId: string): Promise<PermissionFlagDTO[]> {
|
||||
const { flatPermissionFlagMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
@@ -141,13 +33,196 @@ export class PermissionFlagService {
|
||||
},
|
||||
);
|
||||
|
||||
const resultFlags = Object.values(
|
||||
freshFlatPermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf): pf is FlatPermissionFlag =>
|
||||
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
|
||||
);
|
||||
return Object.values(flatPermissionFlagMaps.byUniversalIdentifier)
|
||||
.filter(isDefined)
|
||||
.sort((a, b) => a.createdAt.localeCompare(b.createdAt))
|
||||
.map(fromFlatPermissionFlagToPermissionFlagDto);
|
||||
}
|
||||
|
||||
return resultFlags;
|
||||
async findById(
|
||||
id: string,
|
||||
workspaceId: string,
|
||||
): Promise<PermissionFlagDTO | null> {
|
||||
const { flatPermissionFlagMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatPermissionFlag = findFlatEntityByIdInFlatEntityMaps({
|
||||
flatEntityId: id,
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(flatPermissionFlag)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return fromFlatPermissionFlagToPermissionFlagDto(flatPermissionFlag);
|
||||
}
|
||||
|
||||
async create(
|
||||
input: CreatePermissionFlagInput,
|
||||
workspaceId: string,
|
||||
): Promise<PermissionFlagDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const flatToCreate =
|
||||
fromCreatePermissionFlagInputToFlatPermissionFlagToCreate({
|
||||
createPermissionFlagInput: input,
|
||||
workspaceId,
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
permissionFlag: {
|
||||
flatEntityToCreate: [flatToCreate],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Validation errors occurred while creating permission flag',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatPermissionFlagMaps: recomputedMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatPermissionFlagToPermissionFlagDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: flatToCreate.id,
|
||||
flatEntityMaps: recomputedMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async update(
|
||||
input: UpdatePermissionFlagInput,
|
||||
workspaceId: string,
|
||||
): Promise<PermissionFlagDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const { flatPermissionFlagMaps: existingMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatToUpdate =
|
||||
fromUpdatePermissionFlagInputToFlatPermissionFlagToUpdateOrThrow({
|
||||
flatPermissionFlagMaps: existingMaps,
|
||||
updatePermissionFlagInput: input,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
permissionFlag: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [],
|
||||
flatEntityToUpdate: [flatToUpdate],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Validation errors occurred while updating permission flag',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatPermissionFlagMaps: recomputedMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return fromFlatPermissionFlagToPermissionFlagDto(
|
||||
findFlatEntityByIdInFlatEntityMapsOrThrow({
|
||||
flatEntityId: input.id,
|
||||
flatEntityMaps: recomputedMaps,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
async delete(id: string, workspaceId: string): Promise<PermissionFlagDTO> {
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const { flatPermissionFlagMaps: existingMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const flatToDelete =
|
||||
fromDeletePermissionFlagInputToFlatPermissionFlagOrThrow({
|
||||
flatPermissionFlagMaps: existingMaps,
|
||||
permissionFlagId: id,
|
||||
});
|
||||
|
||||
const validateAndBuildResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
permissionFlag: {
|
||||
flatEntityToCreate: [],
|
||||
flatEntityToDelete: [flatToDelete],
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (validateAndBuildResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
validateAndBuildResult,
|
||||
'Validation errors occurred while deleting permission flag',
|
||||
);
|
||||
}
|
||||
|
||||
return fromFlatPermissionFlagToPermissionFlagDto(flatToDelete);
|
||||
}
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
|
||||
|
||||
export const fromFlatPermissionFlagToPermissionFlagDto = (
|
||||
flatPermissionFlag: FlatPermissionFlag,
|
||||
): PermissionFlagDTO => ({
|
||||
id: flatPermissionFlag.id,
|
||||
roleId: flatPermissionFlag.roleId,
|
||||
flag: flatPermissionFlag.flag,
|
||||
});
|
||||
+22
-13
@@ -1,7 +1,10 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service';
|
||||
import { ApplicationEntity } from 'src/engine/core-modules/application/application.entity';
|
||||
@@ -60,7 +63,7 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
workspaceId: 'test-workspace-id',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
@@ -133,7 +136,7 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
workspaceId: 'test-workspace-id',
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
@@ -184,7 +187,7 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
roleTargets: [],
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
@@ -254,7 +257,7 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
roleTargets: [],
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
@@ -292,8 +295,8 @@ describe('PermissionsService', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Granular permissions with permissionFlags', () => {
|
||||
it('should grant specific tool permission when included in permissionFlags even if canAccessAllTools is false', () => {
|
||||
describe('Granular permissions with rolePermissionFlags', () => {
|
||||
it('should grant specific tool permission when included in rolePermissionFlags even if canAccessAllTools is false', () => {
|
||||
const roleWithSpecificPermission: Partial<RoleEntity> = {
|
||||
id: 'test-role-id',
|
||||
label: 'Test Role',
|
||||
@@ -308,10 +311,13 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [
|
||||
rolePermissionFlags: [
|
||||
{
|
||||
id: 'permission-1',
|
||||
flag: PermissionFlagType.UPLOAD_FILE,
|
||||
permissionFlag: {
|
||||
key: PermissionFlagType.UPLOAD_FILE,
|
||||
universalIdentifier: SystemPermissionFlag.UPLOAD_FILE,
|
||||
},
|
||||
roleId: 'test-role-id',
|
||||
workspaceId: 'test-workspace-id',
|
||||
createdAt: new Date(),
|
||||
@@ -338,7 +344,7 @@ describe('PermissionsService', () => {
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('should grant specific settings permission when included in permissionFlags even if canUpdateAllSettings is false', () => {
|
||||
it('should grant specific settings permission when included in rolePermissionFlags even if canUpdateAllSettings is false', () => {
|
||||
const roleWithSpecificPermission: Partial<RoleEntity> = {
|
||||
id: 'test-role-id',
|
||||
label: 'Test Role',
|
||||
@@ -353,10 +359,13 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [
|
||||
rolePermissionFlags: [
|
||||
{
|
||||
id: 'permission-1',
|
||||
flag: PermissionFlagType.ROLES,
|
||||
permissionFlag: {
|
||||
key: PermissionFlagType.ROLES,
|
||||
universalIdentifier: SystemPermissionFlag.ROLES,
|
||||
},
|
||||
roleId: 'test-role-id',
|
||||
workspaceId: 'test-workspace-id',
|
||||
createdAt: new Date(),
|
||||
@@ -400,7 +409,7 @@ describe('PermissionsService', () => {
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
roleTargets: [],
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
|
||||
+32
-17
@@ -2,7 +2,10 @@ import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { In, Repository } from 'typeorm';
|
||||
|
||||
@@ -66,7 +69,7 @@ export class PermissionsService {
|
||||
|
||||
const defaultSettingsPermissions =
|
||||
this.getDefaultUserWorkspacePermissions().permissionFlags;
|
||||
const permissionFlags = Object.keys(PermissionFlagType).reduce(
|
||||
const permissionFlags = Object.values(PermissionFlagType).reduce(
|
||||
(acc, feature) => {
|
||||
const hasBasePermission = this.isToolPermission(feature)
|
||||
? roleOfUserWorkspace.canAccessAllTools
|
||||
@@ -76,9 +79,7 @@ export class PermissionsService {
|
||||
...acc,
|
||||
[feature]:
|
||||
hasBasePermission ||
|
||||
roleOfUserWorkspace.permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === feature,
|
||||
),
|
||||
this.roleHasPermissionFlag(roleOfUserWorkspace, feature),
|
||||
};
|
||||
},
|
||||
defaultSettingsPermissions,
|
||||
@@ -150,7 +151,10 @@ export class PermissionsService {
|
||||
|
||||
const role = await this.roleRepository.findOne({
|
||||
where: { id: roleId, workspaceId },
|
||||
relations: ['permissionFlags'],
|
||||
relations: [
|
||||
'rolePermissionFlags',
|
||||
'rolePermissionFlags.permissionFlag',
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDefined(role)) {
|
||||
@@ -203,7 +207,10 @@ export class PermissionsService {
|
||||
|
||||
const role = await this.roleRepository.findOne({
|
||||
where: { id: applicationRoleId, workspaceId },
|
||||
relations: ['permissionFlags'],
|
||||
relations: [
|
||||
'rolePermissionFlags',
|
||||
'rolePermissionFlags.permissionFlag',
|
||||
],
|
||||
});
|
||||
|
||||
if (!isDefined(role)) {
|
||||
@@ -240,10 +247,22 @@ export class PermissionsService {
|
||||
return true;
|
||||
}
|
||||
|
||||
const permissionFlags = role.permissionFlags ?? [];
|
||||
return this.roleHasPermissionFlag(role, setting);
|
||||
}
|
||||
|
||||
return permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === setting,
|
||||
private roleHasPermissionFlag(
|
||||
role: RoleEntity,
|
||||
flag: PermissionFlagType,
|
||||
): boolean {
|
||||
const rolePermissionFlags = role.rolePermissionFlags ?? [];
|
||||
|
||||
const permissionFlagUniversalIdentifier = SystemPermissionFlag[flag];
|
||||
|
||||
return rolePermissionFlags.some(
|
||||
(rolePermissionFlag) =>
|
||||
(rolePermissionFlag.permissionFlag?.universalIdentifier ??
|
||||
SystemPermissionFlag[rolePermissionFlag.flag]) ===
|
||||
permissionFlagUniversalIdentifier,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -292,7 +311,7 @@ export class PermissionsService {
|
||||
const result = await this.getRolesFromPermissionConfig(
|
||||
rolePermissionConfig,
|
||||
workspaceId,
|
||||
['permissionFlags'],
|
||||
['rolePermissionFlags', 'rolePermissionFlags.permissionFlag'],
|
||||
);
|
||||
|
||||
if (result === null) {
|
||||
@@ -318,7 +337,7 @@ export class PermissionsService {
|
||||
const result = await this.getRolesFromPermissionConfig(
|
||||
rolePermissionConfig,
|
||||
workspaceId,
|
||||
['permissionFlags'],
|
||||
['rolePermissionFlags', 'rolePermissionFlags.permissionFlag'],
|
||||
);
|
||||
|
||||
if (result === null) {
|
||||
@@ -332,11 +351,7 @@ export class PermissionsService {
|
||||
return true;
|
||||
}
|
||||
|
||||
const permissionFlags = role.permissionFlags ?? [];
|
||||
|
||||
return permissionFlags.some(
|
||||
(permissionFlag) => permissionFlag.flag === flag,
|
||||
);
|
||||
return this.roleHasPermissionFlag(role, flag);
|
||||
};
|
||||
|
||||
return useIntersection
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
import { type PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
export type CreateRolePermissionFlagInput = {
|
||||
roleId: string;
|
||||
permissionFlagId: string;
|
||||
flag: PermissionFlagType;
|
||||
universalIdentifier?: string;
|
||||
};
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import { Field, ObjectType } from '@nestjs/graphql';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { UUIDScalarType } from 'src/engine/api/graphql/workspace-schema-builder/graphql-types/scalars';
|
||||
|
||||
@ObjectType('RolePermissionFlag')
|
||||
export class RolePermissionFlagDTO {
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
id: string;
|
||||
|
||||
@Field(() => UUIDScalarType, { nullable: false })
|
||||
roleId: string;
|
||||
|
||||
@Field({ nullable: false })
|
||||
flag: PermissionFlagType;
|
||||
}
|
||||
+61
@@ -0,0 +1,61 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
Column,
|
||||
CreateDateColumn,
|
||||
Entity,
|
||||
Index,
|
||||
JoinColumn,
|
||||
ManyToOne,
|
||||
PrimaryGeneratedColumn,
|
||||
Relation,
|
||||
Unique,
|
||||
UpdateDateColumn,
|
||||
} from 'typeorm';
|
||||
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { SyncableEntity } from 'src/engine/workspace-manager/types/syncable-entity.interface';
|
||||
|
||||
@Entity('rolePermissionFlag')
|
||||
@Unique('IDX_ROLE_PERMISSION_FLAG_FLAG_ROLE_ID_UNIQUE', ['flag', 'roleId'])
|
||||
@Unique('IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID_ROLE_ID_UNIQUE', [
|
||||
'permissionFlagId',
|
||||
'roleId',
|
||||
])
|
||||
@Index('IDX_ROLE_PERMISSION_FLAG_ROLE_ID', ['roleId'])
|
||||
@Index('IDX_ROLE_PERMISSION_FLAG_PERMISSION_FLAG_ID', ['permissionFlagId'])
|
||||
export class RolePermissionFlagEntity extends SyncableEntity {
|
||||
@PrimaryGeneratedColumn('uuid')
|
||||
id: string;
|
||||
|
||||
@Column({ nullable: false, type: 'uuid' })
|
||||
roleId: string;
|
||||
|
||||
@ManyToOne(() => RoleEntity, (role) => role.rolePermissionFlags, {
|
||||
onDelete: 'CASCADE',
|
||||
})
|
||||
@JoinColumn({ name: 'roleId' })
|
||||
role: Relation<RoleEntity>;
|
||||
|
||||
@Column({ nullable: false, type: 'varchar' })
|
||||
flag: PermissionFlagType;
|
||||
|
||||
@Column({ nullable: true, type: 'uuid' })
|
||||
permissionFlagId: string | null;
|
||||
|
||||
@ManyToOne(
|
||||
() => PermissionFlagEntity,
|
||||
(permissionFlag) => permissionFlag.rolePermissionFlags,
|
||||
{
|
||||
onDelete: 'CASCADE',
|
||||
},
|
||||
)
|
||||
@JoinColumn({ name: 'permissionFlagId' })
|
||||
permissionFlag: Relation<PermissionFlagEntity> | null;
|
||||
|
||||
@CreateDateColumn({ type: 'timestamptz' })
|
||||
createdAt: Date;
|
||||
|
||||
@UpdateDateColumn({ type: 'timestamptz' })
|
||||
updatedAt: Date;
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
|
||||
import { ApplicationModule } from 'src/engine/core-modules/application/application.module';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { RolePermissionFlagService } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.service';
|
||||
import { WorkspaceCacheModule } from 'src/engine/workspace-cache/workspace-cache.module';
|
||||
import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace-migration/workspace-migration.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
WorkspaceCacheModule,
|
||||
ApplicationModule,
|
||||
WorkspaceMigrationModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
],
|
||||
providers: [RolePermissionFlagService],
|
||||
exports: [RolePermissionFlagService],
|
||||
})
|
||||
export class RolePermissionFlagModule {}
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg } from '@lingui/core/macro';
|
||||
import {
|
||||
type PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { ApplicationService } from 'src/engine/core-modules/application/application.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { fromCreateRolePermissionFlagInputToFlatRolePermissionFlagToCreate } from 'src/engine/metadata-modules/flat-role-permission-flag/utils/from-create-role-permission-flag-input-to-flat-role-permission-flag-to-create.util';
|
||||
import { type UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/role-permission-flag/dtos/upsert-permission-flags.input';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { WorkspaceMigrationBuilderException } from 'src/engine/workspace-manager/workspace-migration/exceptions/workspace-migration-builder-exception';
|
||||
import { WorkspaceMigrationValidateBuildAndRunService } from 'src/engine/workspace-manager/workspace-migration/services/workspace-migration-validate-build-and-run-service';
|
||||
|
||||
@Injectable()
|
||||
export class RolePermissionFlagService {
|
||||
constructor(
|
||||
private readonly workspaceMigrationValidateBuildAndRunService: WorkspaceMigrationValidateBuildAndRunService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly applicationService: ApplicationService,
|
||||
) {}
|
||||
|
||||
public async upsertPermissionFlags({
|
||||
workspaceId,
|
||||
input,
|
||||
}: {
|
||||
workspaceId: string;
|
||||
input: UpsertPermissionFlagsInput;
|
||||
}): Promise<FlatRolePermissionFlag[]> {
|
||||
const { flatPermissionFlagMaps, flatRolePermissionFlagMaps, flatRoleMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: [
|
||||
'flatPermissionFlagMaps',
|
||||
'flatRolePermissionFlagMaps',
|
||||
'flatRoleMaps',
|
||||
],
|
||||
},
|
||||
);
|
||||
|
||||
const roleUniversalId = flatRoleMaps.universalIdentifierById[input.roleId];
|
||||
const role = isDefined(roleUniversalId)
|
||||
? flatRoleMaps.byUniversalIdentifier[roleUniversalId]
|
||||
: undefined;
|
||||
|
||||
if (!isDefined(role)) {
|
||||
throw new PermissionsException(
|
||||
PermissionsExceptionMessage.ROLE_NOT_FOUND,
|
||||
PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
{
|
||||
userFriendlyMessage: msg`The role you are trying to modify could not be found.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const roleUniversalIdentifier = role.universalIdentifier;
|
||||
const permissionFlagByKey = new Map(
|
||||
Object.values(flatPermissionFlagMaps.byUniversalIdentifier)
|
||||
.filter(isDefined)
|
||||
.map((permissionFlag) => [permissionFlag.key, permissionFlag]),
|
||||
);
|
||||
const permissionFlagsByInputKey = input.permissionFlagKeys.map((flag) => ({
|
||||
flag,
|
||||
permissionFlag: permissionFlagByKey.get(flag),
|
||||
}));
|
||||
const missingPermissionFlags = permissionFlagsByInputKey
|
||||
.filter(({ permissionFlag }) => !isDefined(permissionFlag))
|
||||
.map(({ flag }) => flag);
|
||||
|
||||
if (missingPermissionFlags.length > 0) {
|
||||
throw new PermissionsException(
|
||||
`${PermissionsExceptionMessage.INVALID_SETTING}: ${missingPermissionFlags.join(', ')}`,
|
||||
PermissionsExceptionCode.INVALID_SETTING,
|
||||
{
|
||||
userFriendlyMessage: msg`Some of the permissions you selected are not valid. Please try again with valid permission settings.`,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
const currentRolePermissionFlagsForRole = Object.values(
|
||||
flatRolePermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf): pf is FlatRolePermissionFlag =>
|
||||
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
|
||||
);
|
||||
|
||||
const inputSet = new Set(
|
||||
permissionFlagsByInputKey
|
||||
.map(({ permissionFlag }) => permissionFlag?.universalIdentifier)
|
||||
.filter(isDefined),
|
||||
);
|
||||
const getEffectiveUniversalIdentifier = (
|
||||
pf: FlatRolePermissionFlag,
|
||||
): string =>
|
||||
pf.permissionFlagUniversalIdentifier ?? SystemPermissionFlag[pf.flag];
|
||||
const existingSet = new Set(
|
||||
currentRolePermissionFlagsForRole.map(getEffectiveUniversalIdentifier),
|
||||
);
|
||||
|
||||
const { workspaceCustomFlatApplication } =
|
||||
await this.applicationService.findWorkspaceTwentyStandardAndCustomApplicationOrThrow(
|
||||
{ workspaceId },
|
||||
);
|
||||
|
||||
const flatEntityToCreate = permissionFlagsByInputKey
|
||||
.map(({ permissionFlag }) => permissionFlag)
|
||||
.filter(isDefined)
|
||||
.filter(
|
||||
(permissionFlag) =>
|
||||
!existingSet.has(permissionFlag.universalIdentifier),
|
||||
)
|
||||
.map((permissionFlag) =>
|
||||
fromCreateRolePermissionFlagInputToFlatRolePermissionFlagToCreate({
|
||||
createRolePermissionFlagInput: {
|
||||
roleId: input.roleId,
|
||||
permissionFlagId: permissionFlag.id,
|
||||
flag: permissionFlag.key as PermissionFlagType,
|
||||
},
|
||||
flatApplication: workspaceCustomFlatApplication,
|
||||
flatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
}),
|
||||
);
|
||||
|
||||
const flatEntityToDelete = currentRolePermissionFlagsForRole.filter(
|
||||
(pf) => !inputSet.has(getEffectiveUniversalIdentifier(pf)),
|
||||
);
|
||||
|
||||
if (flatEntityToCreate.length === 0 && flatEntityToDelete.length === 0) {
|
||||
return currentRolePermissionFlagsForRole.filter((pf) =>
|
||||
inputSet.has(getEffectiveUniversalIdentifier(pf)),
|
||||
);
|
||||
}
|
||||
|
||||
const buildAndRunResult =
|
||||
await this.workspaceMigrationValidateBuildAndRunService.validateBuildAndRunWorkspaceMigration(
|
||||
{
|
||||
allFlatEntityOperationByMetadataName: {
|
||||
rolePermissionFlag: {
|
||||
flatEntityToCreate,
|
||||
flatEntityToDelete,
|
||||
flatEntityToUpdate: [],
|
||||
},
|
||||
},
|
||||
workspaceId,
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
workspaceCustomFlatApplication.universalIdentifier,
|
||||
},
|
||||
);
|
||||
|
||||
if (buildAndRunResult.status === 'fail') {
|
||||
throw new WorkspaceMigrationBuilderException(
|
||||
buildAndRunResult,
|
||||
'Validation errors occurred while upserting permission flags',
|
||||
);
|
||||
}
|
||||
|
||||
const { flatRolePermissionFlagMaps: freshFlatRolePermissionFlagMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId,
|
||||
flatMapsKeys: ['flatRolePermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
const resultFlags = Object.values(
|
||||
freshFlatRolePermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf): pf is FlatRolePermissionFlag =>
|
||||
isDefined(pf) && pf.roleUniversalIdentifier === roleUniversalIdentifier,
|
||||
);
|
||||
|
||||
return resultFlags;
|
||||
}
|
||||
}
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { fromFlatRolePermissionFlagToRolePermissionFlagDto } from 'src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util';
|
||||
|
||||
const buildFlatRolePermissionFlag = (
|
||||
overrides: Partial<FlatRolePermissionFlag> = {},
|
||||
): FlatRolePermissionFlag =>
|
||||
({
|
||||
id: 'role-permission-flag-id',
|
||||
roleId: 'role-id',
|
||||
workspaceId: 'workspace-id',
|
||||
applicationId: 'application-id',
|
||||
applicationUniversalIdentifier: 'application-universal-id',
|
||||
permissionFlagUniversalIdentifier: 'permission-flag-universal-id',
|
||||
flag: PermissionFlagType.WORKSPACE,
|
||||
...overrides,
|
||||
}) as FlatRolePermissionFlag;
|
||||
|
||||
const buildFlatPermissionFlag = (): FlatPermissionFlag => ({
|
||||
id: 'permission-flag-id',
|
||||
workspaceId: 'workspace-id',
|
||||
applicationId: 'application-id',
|
||||
universalIdentifier: 'permission-flag-universal-id',
|
||||
applicationUniversalIdentifier: 'application-universal-id',
|
||||
key: PermissionFlagType.WORKSPACE,
|
||||
label: 'Workspace',
|
||||
description: null,
|
||||
icon: null,
|
||||
permissionType: 'settings',
|
||||
rolePermissionFlagIds: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
createdAt: '2026-05-13T00:00:00.000Z',
|
||||
updatedAt: '2026-05-13T00:00:00.000Z',
|
||||
});
|
||||
|
||||
const buildFlatPermissionFlagMaps = (): FlatPermissionFlagMaps => ({
|
||||
byUniversalIdentifier: {
|
||||
'permission-flag-universal-id': buildFlatPermissionFlag(),
|
||||
},
|
||||
universalIdentifierById: {
|
||||
'permission-flag-id': 'permission-flag-universal-id',
|
||||
},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
});
|
||||
|
||||
describe('fromFlatRolePermissionFlagToRolePermissionFlagDto', () => {
|
||||
it('maps role permission flags to the public permission flag DTO shape', () => {
|
||||
expect(
|
||||
fromFlatRolePermissionFlagToRolePermissionFlagDto(
|
||||
buildFlatRolePermissionFlag(),
|
||||
buildFlatPermissionFlagMaps(),
|
||||
),
|
||||
).toEqual({
|
||||
id: 'role-permission-flag-id',
|
||||
roleId: 'role-id',
|
||||
flag: PermissionFlagType.WORKSPACE,
|
||||
});
|
||||
});
|
||||
|
||||
it('falls back to the legacy flag column when the catalog is missing the entry', () => {
|
||||
expect(
|
||||
fromFlatRolePermissionFlagToRolePermissionFlagDto(
|
||||
buildFlatRolePermissionFlag(),
|
||||
{
|
||||
byUniversalIdentifier: {},
|
||||
universalIdentifierById: {},
|
||||
universalIdentifiersByApplicationId: {},
|
||||
},
|
||||
),
|
||||
).toEqual({
|
||||
id: 'role-permission-flag-id',
|
||||
roleId: 'role-id',
|
||||
flag: PermissionFlagType.WORKSPACE,
|
||||
});
|
||||
});
|
||||
});
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { type FlatPermissionFlagMaps } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag-maps.type';
|
||||
import { synthesizeFlatPermissionFlagFromFlag } from 'src/engine/metadata-modules/flat-permission-flag/utils/synthesize-flat-permission-flag-from-flag.util';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { type RolePermissionFlagDTO } from 'src/engine/metadata-modules/role-permission-flag/dtos/role-permission-flag.dto';
|
||||
|
||||
export const fromFlatRolePermissionFlagToRolePermissionFlagDto = (
|
||||
flatRolePermissionFlag: FlatRolePermissionFlag,
|
||||
flatPermissionFlagMaps: FlatPermissionFlagMaps,
|
||||
): RolePermissionFlagDTO => {
|
||||
const catalogPermissionFlag = isDefined(
|
||||
flatRolePermissionFlag.permissionFlagUniversalIdentifier,
|
||||
)
|
||||
? flatPermissionFlagMaps.byUniversalIdentifier[
|
||||
flatRolePermissionFlag.permissionFlagUniversalIdentifier
|
||||
]
|
||||
: undefined;
|
||||
|
||||
const permissionFlag = isDefined(catalogPermissionFlag)
|
||||
? catalogPermissionFlag
|
||||
: synthesizeFlatPermissionFlagFromFlag({
|
||||
flag: flatRolePermissionFlag.flag,
|
||||
workspaceId: flatRolePermissionFlag.workspaceId,
|
||||
applicationId: flatRolePermissionFlag.applicationId,
|
||||
applicationUniversalIdentifier:
|
||||
flatRolePermissionFlag.applicationUniversalIdentifier,
|
||||
});
|
||||
|
||||
return {
|
||||
id: flatRolePermissionFlag.id,
|
||||
roleId: flatRolePermissionFlag.roleId,
|
||||
flag: permissionFlag.key as PermissionFlagType,
|
||||
};
|
||||
};
|
||||
@@ -7,7 +7,7 @@ import { WorkspaceMemberDTO } from 'src/engine/core-modules/user/dtos/workspace-
|
||||
import { AgentDTO } from 'src/engine/metadata-modules/ai/ai-agent/dtos/agent.dto';
|
||||
import { FieldPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/field-permission.dto';
|
||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
||||
import { PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
|
||||
import { RolePermissionFlagDTO } from 'src/engine/metadata-modules/role-permission-flag/dtos/role-permission-flag.dto';
|
||||
import { type RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RowLevelPermissionPredicateGroupDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate-group.dto';
|
||||
import { RowLevelPermissionPredicateDTO } from 'src/engine/metadata-modules/row-level-permission-predicate/dtos/row-level-permission-predicate.dto';
|
||||
@@ -86,8 +86,8 @@ export class RoleDTO {
|
||||
@Field({ nullable: false })
|
||||
canDestroyAllObjectRecords: boolean;
|
||||
|
||||
@Field(() => [PermissionFlagDTO], { nullable: true })
|
||||
permissionFlags?: PermissionFlagDTO[];
|
||||
@Field(() => [RolePermissionFlagDTO], { nullable: true })
|
||||
permissionFlags?: RolePermissionFlagDTO[];
|
||||
|
||||
@Field(() => [ObjectPermissionDTO], { nullable: true })
|
||||
objectPermissions?: ObjectPermissionDTO[];
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
@@ -81,10 +81,10 @@ export class RoleEntity extends SyncableEntity implements Required<RoleEntity> {
|
||||
objectPermissions: Relation<ObjectPermissionEntity[]>;
|
||||
|
||||
@OneToMany(
|
||||
() => PermissionFlagEntity,
|
||||
(permissionFlag: PermissionFlagEntity) => permissionFlag.role,
|
||||
() => RolePermissionFlagEntity,
|
||||
(rolePermissionFlag: RolePermissionFlagEntity) => rolePermissionFlag.role,
|
||||
)
|
||||
permissionFlags: Relation<PermissionFlagEntity[]>;
|
||||
rolePermissionFlags: Relation<RolePermissionFlagEntity[]>;
|
||||
|
||||
@OneToMany(
|
||||
() => FieldPermissionEntity,
|
||||
|
||||
@@ -15,8 +15,8 @@ import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadat
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { ObjectPermissionModule } from 'src/engine/metadata-modules/object-permission/object-permission.module';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { PermissionFlagModule } from 'src/engine/metadata-modules/permission-flag/permission-flag.module';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RolePermissionFlagModule } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
@@ -39,7 +39,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
RoleEntity,
|
||||
RoleTargetEntity,
|
||||
ObjectPermissionEntity,
|
||||
PermissionFlagEntity,
|
||||
RolePermissionFlagEntity,
|
||||
FieldPermissionEntity,
|
||||
UserWorkspaceEntity,
|
||||
ObjectMetadataEntity,
|
||||
@@ -52,7 +52,7 @@ import { WorkspaceMigrationModule } from 'src/engine/workspace-manager/workspace
|
||||
ApiKeyModule,
|
||||
PermissionsModule,
|
||||
ObjectPermissionModule,
|
||||
PermissionFlagModule,
|
||||
RolePermissionFlagModule,
|
||||
RowLevelPermissionModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
WorkspaceMigrationModule,
|
||||
|
||||
@@ -33,16 +33,17 @@ import { AgentDTO } from 'src/engine/metadata-modules/ai/ai-agent/dtos/agent.dto
|
||||
import { fromFlatAgentWithRoleIdToAgentDto } from 'src/engine/metadata-modules/flat-agent/utils/from-agent-entity-to-agent-dto.util';
|
||||
import { FieldPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/field-permission.dto';
|
||||
import { ObjectPermissionDTO } from 'src/engine/metadata-modules/object-permission/dtos/object-permission.dto';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { UpsertFieldPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-field-permissions.input';
|
||||
import { UpsertObjectPermissionsInput } from 'src/engine/metadata-modules/object-permission/dtos/upsert-object-permissions.input';
|
||||
import { FieldPermissionService } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.service';
|
||||
import { ObjectPermissionService } from 'src/engine/metadata-modules/object-permission/object-permission.service';
|
||||
import { fromFlatFieldPermissionToFieldPermissionDto } from 'src/engine/metadata-modules/object-permission/utils/from-flat-field-permission-to-field-permission-dto.util';
|
||||
import { fromFlatObjectPermissionToObjectPermissionDto } from 'src/engine/metadata-modules/object-permission/utils/from-flat-object-permission-to-object-permission-dto.util';
|
||||
import { PermissionFlagDTO } from 'src/engine/metadata-modules/permission-flag/dtos/permission-flag.dto';
|
||||
import { UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/permission-flag/dtos/upsert-permission-flag-input';
|
||||
import { PermissionFlagService } from 'src/engine/metadata-modules/permission-flag/permission-flag.service';
|
||||
import { fromFlatPermissionFlagToPermissionFlagDto } from 'src/engine/metadata-modules/permission-flag/utils/from-flat-permission-flag-to-permission-flag-dto.util';
|
||||
import { RolePermissionFlagDTO } from 'src/engine/metadata-modules/role-permission-flag/dtos/role-permission-flag.dto';
|
||||
import { UpsertPermissionFlagsInput } from 'src/engine/metadata-modules/role-permission-flag/dtos/upsert-permission-flags.input';
|
||||
import { RolePermissionFlagService } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.service';
|
||||
import { fromFlatRolePermissionFlagToRolePermissionFlagDto } from 'src/engine/metadata-modules/role-permission-flag/utils/from-flat-role-permission-flag-to-role-permission-flag-dto.util';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
@@ -87,7 +88,7 @@ export class RoleResolver {
|
||||
private readonly roleService: RoleService,
|
||||
private readonly userWorkspaceService: UserWorkspaceService,
|
||||
private readonly objectPermissionService: ObjectPermissionService,
|
||||
private readonly settingPermissionService: PermissionFlagService,
|
||||
private readonly rolePermissionFlagService: RolePermissionFlagService,
|
||||
private readonly agentRoleService: AiAgentRoleService,
|
||||
private readonly apiKeyRoleService: ApiKeyRoleService,
|
||||
private readonly fieldPermissionService: FieldPermissionService,
|
||||
@@ -95,6 +96,7 @@ export class RoleResolver {
|
||||
private readonly rowLevelPermissionPredicateService: RowLevelPermissionPredicateService,
|
||||
private readonly rowLevelPermissionPredicateGroupService: RowLevelPermissionPredicateGroupService,
|
||||
private readonly workspaceCacheService: WorkspaceCacheService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
) {}
|
||||
|
||||
@Query(() => [RoleDTO])
|
||||
@@ -223,18 +225,31 @@ export class RoleResolver {
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => [PermissionFlagDTO])
|
||||
@Mutation(() => [RolePermissionFlagDTO])
|
||||
async upsertPermissionFlags(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@Args('upsertPermissionFlagsInput')
|
||||
upsertPermissionFlagsInput: UpsertPermissionFlagsInput,
|
||||
): Promise<PermissionFlagDTO[]> {
|
||||
const flatPermissionFlags =
|
||||
await this.settingPermissionService.upsertPermissionFlags({
|
||||
): Promise<RolePermissionFlagDTO[]> {
|
||||
const flatRolePermissionFlags =
|
||||
await this.rolePermissionFlagService.upsertPermissionFlags({
|
||||
workspaceId: workspace.id,
|
||||
input: upsertPermissionFlagsInput,
|
||||
});
|
||||
return flatPermissionFlags.map(fromFlatPermissionFlagToPermissionFlagDto);
|
||||
const { flatPermissionFlagMaps } =
|
||||
await this.workspaceManyOrAllFlatEntityMapsCacheService.getOrRecomputeManyOrAllFlatEntityMaps(
|
||||
{
|
||||
workspaceId: workspace.id,
|
||||
flatMapsKeys: ['flatPermissionFlagMaps'],
|
||||
},
|
||||
);
|
||||
|
||||
return flatRolePermissionFlags.map((flatRolePermissionFlag) =>
|
||||
fromFlatRolePermissionFlagToRolePermissionFlagDto(
|
||||
flatRolePermissionFlag,
|
||||
flatPermissionFlagMaps,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@Mutation(() => [FieldPermissionDTO])
|
||||
|
||||
@@ -49,7 +49,9 @@ export class RoleService {
|
||||
},
|
||||
relations: {
|
||||
roleTargets: true,
|
||||
permissionFlags: true,
|
||||
rolePermissionFlags: {
|
||||
permissionFlag: true,
|
||||
},
|
||||
objectPermissions: true,
|
||||
fieldPermissions: true,
|
||||
},
|
||||
@@ -67,7 +69,9 @@ export class RoleService {
|
||||
},
|
||||
relations: {
|
||||
roleTargets: true,
|
||||
permissionFlags: true,
|
||||
rolePermissionFlags: {
|
||||
permissionFlag: true,
|
||||
},
|
||||
objectPermissions: true,
|
||||
fieldPermissions: true,
|
||||
},
|
||||
|
||||
+30
-21
@@ -1,14 +1,17 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
import { getRepositoryToken } from '@nestjs/typeorm';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import { type Repository } from 'typeorm';
|
||||
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { WorkspaceRolesPermissionsCacheService } from 'src/engine/metadata-modules/role/services/workspace-roles-permissions-cache.service';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
@@ -23,7 +26,7 @@ const PERSON_OBJECT_METADATA_ID = '44444444-4444-4444-8444-444444444444';
|
||||
|
||||
const createBaseRole = (
|
||||
overrides: Partial<RoleEntity> &
|
||||
Pick<RoleEntity, 'permissionFlags' | 'objectPermissions'>,
|
||||
Pick<RoleEntity, 'rolePermissionFlags' | 'objectPermissions'>,
|
||||
): RoleEntity =>
|
||||
({
|
||||
id: ROLE_ID,
|
||||
@@ -56,8 +59,8 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
let objectPermissionRepository: jest.Mocked<
|
||||
Pick<Repository<ObjectPermissionEntity>, 'find'>
|
||||
>;
|
||||
let permissionFlagRepository: jest.Mocked<
|
||||
Pick<Repository<PermissionFlagEntity>, 'find'>
|
||||
let rolePermissionFlagRepository: jest.Mocked<
|
||||
Pick<Repository<RolePermissionFlagEntity>, 'find'>
|
||||
>;
|
||||
|
||||
const workspaceObjectMetadataFixture: ObjectMetadataEntity[] = [
|
||||
@@ -94,7 +97,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
find: jest.fn().mockResolvedValue([]),
|
||||
};
|
||||
|
||||
permissionFlagRepository = {
|
||||
rolePermissionFlagRepository = {
|
||||
find: jest.fn().mockResolvedValue([]),
|
||||
};
|
||||
const fieldPermissionRepository = {
|
||||
@@ -123,8 +126,8 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
useValue: objectPermissionRepository,
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(PermissionFlagEntity),
|
||||
useValue: permissionFlagRepository,
|
||||
provide: getRepositoryToken(RolePermissionFlagEntity),
|
||||
useValue: rolePermissionFlagRepository,
|
||||
},
|
||||
{
|
||||
provide: getRepositoryToken(FieldPermissionEntity),
|
||||
@@ -148,7 +151,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
it('should deny all record permissions when role has neither workspace members access nor update-all-settings', async () => {
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -164,16 +167,19 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
});
|
||||
|
||||
it('should grant all record permissions when role has WORKSPACE_MEMBERS permission flag', async () => {
|
||||
permissionFlagRepository.find.mockResolvedValue([
|
||||
rolePermissionFlagRepository.find.mockResolvedValue([
|
||||
{
|
||||
roleId: ROLE_ID,
|
||||
flag: PermissionFlagType.WORKSPACE_MEMBERS,
|
||||
} as PermissionFlagEntity,
|
||||
permissionFlag: {
|
||||
key: PermissionFlagType.WORKSPACE_MEMBERS,
|
||||
universalIdentifier: SystemPermissionFlag.WORKSPACE_MEMBERS,
|
||||
},
|
||||
} as RolePermissionFlagEntity,
|
||||
]);
|
||||
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -192,7 +198,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
canUpdateAllSettings: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -210,7 +216,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
it('should deny all record permissions when role has neither workflows access nor update-all-settings', async () => {
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -225,16 +231,19 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
});
|
||||
|
||||
it('should grant all record permissions when role has WORKFLOWS permission flag', async () => {
|
||||
permissionFlagRepository.find.mockResolvedValue([
|
||||
rolePermissionFlagRepository.find.mockResolvedValue([
|
||||
{
|
||||
roleId: ROLE_ID,
|
||||
flag: PermissionFlagType.WORKFLOWS,
|
||||
} as PermissionFlagEntity,
|
||||
permissionFlag: {
|
||||
key: PermissionFlagType.WORKFLOWS,
|
||||
universalIdentifier: SystemPermissionFlag.WORKFLOWS,
|
||||
},
|
||||
} as RolePermissionFlagEntity,
|
||||
]);
|
||||
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -264,7 +273,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
|
||||
roleRepository.find.mockResolvedValue([
|
||||
createBaseRole({
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
@@ -285,7 +294,7 @@ describe('WorkspaceRolesPermissionsCacheService', () => {
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
canDestroyAllObjectRecords: true,
|
||||
permissionFlags: [],
|
||||
rolePermissionFlags: [],
|
||||
objectPermissions: [],
|
||||
}),
|
||||
]);
|
||||
|
||||
+9
-8
@@ -12,7 +12,7 @@ import { type FlatRole } from 'src/engine/metadata-modules/flat-role/types/flat-
|
||||
import { fromRoleEntityToFlatRole } from 'src/engine/metadata-modules/flat-role/utils/from-role-entity-to-flat-role.util';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleTargetEntity } from 'src/engine/metadata-modules/role-target/role-target.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
@@ -36,8 +36,8 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
private readonly roleTargetRepository: Repository<RoleTargetEntity>,
|
||||
@InjectRepository(ObjectPermissionEntity)
|
||||
private readonly objectPermissionRepository: Repository<ObjectPermissionEntity>,
|
||||
@InjectRepository(PermissionFlagEntity)
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
@InjectRepository(RolePermissionFlagEntity)
|
||||
private readonly rolePermissionFlagRepository: Repository<RolePermissionFlagEntity>,
|
||||
@InjectRepository(FieldPermissionEntity)
|
||||
private readonly fieldPermissionRepository: Repository<FieldPermissionEntity>,
|
||||
@InjectRepository(RowLevelPermissionPredicateEntity)
|
||||
@@ -56,7 +56,7 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
applications,
|
||||
roleTargets,
|
||||
objectPermissions,
|
||||
permissionFlags,
|
||||
rolePermissionFlags,
|
||||
fieldPermissions,
|
||||
rowLevelPermissionPredicates,
|
||||
rowLevelPermissionPredicateGroups,
|
||||
@@ -80,7 +80,7 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
select: ['id', 'universalIdentifier', 'roleId'],
|
||||
withDeleted: true,
|
||||
}),
|
||||
this.permissionFlagRepository.find({
|
||||
this.rolePermissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
select: ['id', 'universalIdentifier', 'roleId'],
|
||||
withDeleted: true,
|
||||
@@ -105,7 +105,7 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
const [
|
||||
roleTargetsByRoleId,
|
||||
objectPermissionsByRoleId,
|
||||
permissionFlagsByRoleId,
|
||||
rolePermissionFlagsByRoleId,
|
||||
fieldPermissionsByRoleId,
|
||||
rowLevelPermissionPredicatesByRoleId,
|
||||
rowLevelPermissionPredicateGroupsByRoleId,
|
||||
@@ -120,7 +120,7 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
{
|
||||
entities: permissionFlags,
|
||||
entities: rolePermissionFlags,
|
||||
foreignKey: 'roleId',
|
||||
},
|
||||
{
|
||||
@@ -149,7 +149,8 @@ export class WorkspaceFlatRoleMapCacheService extends WorkspaceCacheProvider<
|
||||
...roleEntity,
|
||||
roleTargets: roleTargetsByRoleId.get(roleEntity.id) || [],
|
||||
objectPermissions: objectPermissionsByRoleId.get(roleEntity.id) || [],
|
||||
permissionFlags: permissionFlagsByRoleId.get(roleEntity.id) || [],
|
||||
rolePermissionFlags:
|
||||
rolePermissionFlagsByRoleId.get(roleEntity.id) || [],
|
||||
fieldPermissions: fieldPermissionsByRoleId.get(roleEntity.id) || [],
|
||||
rowLevelPermissionPredicates:
|
||||
rowLevelPermissionPredicatesByRoleId.get(roleEntity.id) || [],
|
||||
|
||||
+25
-15
@@ -1,7 +1,10 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { InjectRepository } from '@nestjs/typeorm';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import {
|
||||
PermissionFlagType,
|
||||
SystemPermissionFlag,
|
||||
} from 'twenty-shared/constants';
|
||||
import { STANDARD_OBJECTS } from 'twenty-shared/metadata';
|
||||
import {
|
||||
type ObjectsPermissions,
|
||||
@@ -16,7 +19,7 @@ import { WorkspaceCacheProvider } from 'src/engine/workspace-cache/interfaces/wo
|
||||
import { ObjectMetadataEntity } from 'src/engine/metadata-modules/object-metadata/object-metadata.entity';
|
||||
import { FieldPermissionEntity } from 'src/engine/metadata-modules/object-permission/field-permission/field-permission.entity';
|
||||
import { ObjectPermissionEntity } from 'src/engine/metadata-modules/object-permission/object-permission.entity';
|
||||
import { PermissionFlagEntity } from 'src/engine/metadata-modules/permission-flag/permission-flag.entity';
|
||||
import { RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { RowLevelPermissionPredicateGroupEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate-group.entity';
|
||||
import { RowLevelPermissionPredicateEntity } from 'src/engine/metadata-modules/row-level-permission-predicate/entities/row-level-permission-predicate.entity';
|
||||
@@ -41,8 +44,8 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
private readonly roleRepository: Repository<RoleEntity>,
|
||||
@InjectRepository(ObjectPermissionEntity)
|
||||
private readonly objectPermissionRepository: Repository<ObjectPermissionEntity>,
|
||||
@InjectRepository(PermissionFlagEntity)
|
||||
private readonly permissionFlagRepository: Repository<PermissionFlagEntity>,
|
||||
@InjectRepository(RolePermissionFlagEntity)
|
||||
private readonly rolePermissionFlagRepository: Repository<RolePermissionFlagEntity>,
|
||||
@InjectRepository(FieldPermissionEntity)
|
||||
private readonly fieldPermissionRepository: Repository<FieldPermissionEntity>,
|
||||
@InjectRepository(RowLevelPermissionPredicateEntity)
|
||||
@@ -59,7 +62,7 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
const [
|
||||
roles,
|
||||
objectPermissions,
|
||||
permissionFlags,
|
||||
rolePermissionFlags,
|
||||
fieldPermissions,
|
||||
rowLevelPermissionPredicates,
|
||||
rowLevelPermissionPredicateGroups,
|
||||
@@ -71,8 +74,9 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
this.objectPermissionRepository.find({
|
||||
where: { workspaceId },
|
||||
}),
|
||||
this.permissionFlagRepository.find({
|
||||
this.rolePermissionFlagRepository.find({
|
||||
where: { workspaceId },
|
||||
relations: ['permissionFlag'],
|
||||
}),
|
||||
this.fieldPermissionRepository.find({
|
||||
where: { workspaceId },
|
||||
@@ -91,9 +95,9 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
entities: objectPermissions,
|
||||
foreignKey: 'roleId',
|
||||
});
|
||||
const permissionFlagsByRoleId =
|
||||
regroupEntitiesByRelatedEntityId<'permissionFlag'>({
|
||||
entities: permissionFlags,
|
||||
const rolePermissionFlagsByRoleId =
|
||||
regroupEntitiesByRelatedEntityId<'rolePermissionFlag'>({
|
||||
entities: rolePermissionFlags,
|
||||
foreignKey: 'roleId',
|
||||
});
|
||||
const fieldPermissionsByRoleId =
|
||||
@@ -117,7 +121,8 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
for (const role of roles) {
|
||||
const roleObjectPermissions =
|
||||
objectPermissionsByRoleId.get(role.id) ?? [];
|
||||
const rolePermissionFlags = permissionFlagsByRoleId.get(role.id) ?? [];
|
||||
const roleRolePermissionFlags =
|
||||
rolePermissionFlagsByRoleId.get(role.id) ?? [];
|
||||
const roleFieldPermissions = fieldPermissionsByRoleId.get(role.id) ?? [];
|
||||
const roleRowLevelPermissionPredicates =
|
||||
rowLevelPermissionPredicatesByRoleId.get(role.id) ?? [];
|
||||
@@ -150,7 +155,7 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
const hasWorkflowsPermissions =
|
||||
this.hasSettingsGatedObjectPermissions(
|
||||
role,
|
||||
rolePermissionFlags,
|
||||
roleRolePermissionFlags,
|
||||
PermissionFlagType.WORKFLOWS,
|
||||
);
|
||||
|
||||
@@ -163,7 +168,7 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
const hasWorkspaceMembersPermissions =
|
||||
this.hasSettingsGatedObjectPermissions(
|
||||
role,
|
||||
rolePermissionFlags,
|
||||
roleRolePermissionFlags,
|
||||
PermissionFlagType.WORKSPACE_MEMBERS,
|
||||
);
|
||||
|
||||
@@ -269,13 +274,18 @@ export class WorkspaceRolesPermissionsCacheService extends WorkspaceCacheProvide
|
||||
|
||||
private hasSettingsGatedObjectPermissions(
|
||||
role: RoleEntity,
|
||||
permissionFlags: PermissionFlagEntity[],
|
||||
rolePermissionFlags: RolePermissionFlagEntity[],
|
||||
permissionFlagType: PermissionFlagType,
|
||||
): boolean {
|
||||
const hasPermissionFromRole = role.canUpdateAllSettings;
|
||||
const permissionFlagUniversalIdentifier =
|
||||
SystemPermissionFlag[permissionFlagType];
|
||||
const hasPermissionFromSettingPermissions = isDefined(
|
||||
permissionFlags.find(
|
||||
(permissionFlag) => permissionFlag.flag === permissionFlagType,
|
||||
rolePermissionFlags.find(
|
||||
(rolePermissionFlag) =>
|
||||
(rolePermissionFlag.permissionFlag?.universalIdentifier ??
|
||||
SystemPermissionFlag[rolePermissionFlag.flag]) ===
|
||||
permissionFlagUniversalIdentifier,
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { fromRoleEntityToRoleDto } from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||
|
||||
const buildRoleEntity = (overrides: Partial<RoleEntity> = {}): RoleEntity =>
|
||||
({
|
||||
id: 'role-id',
|
||||
label: 'Role',
|
||||
universalIdentifier: 'role-universal-identifier',
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: false,
|
||||
description: null,
|
||||
icon: null,
|
||||
isEditable: true,
|
||||
canReadAllObjectRecords: false,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canDestroyAllObjectRecords: false,
|
||||
canBeAssignedToUsers: true,
|
||||
canBeAssignedToAgents: true,
|
||||
canBeAssignedToApiKeys: true,
|
||||
roleTargets: [],
|
||||
objectPermissions: [],
|
||||
fieldPermissions: [],
|
||||
rolePermissionFlags: [],
|
||||
...overrides,
|
||||
}) as RoleEntity;
|
||||
|
||||
describe('fromRoleEntityToRoleDto', () => {
|
||||
it('returns undefined permission flags when role permission flags are not loaded', () => {
|
||||
const roleDto = fromRoleEntityToRoleDto(
|
||||
buildRoleEntity({
|
||||
rolePermissionFlags:
|
||||
undefined as unknown as RoleEntity['rolePermissionFlags'],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(roleDto.permissionFlags).toBeUndefined();
|
||||
});
|
||||
|
||||
it('maps loaded role permission flags to the public permission flag DTO shape', () => {
|
||||
const roleDto = fromRoleEntityToRoleDto(
|
||||
buildRoleEntity({
|
||||
rolePermissionFlags: [
|
||||
{
|
||||
id: 'role-permission-flag-id',
|
||||
roleId: 'role-id',
|
||||
permissionFlag: {
|
||||
key: PermissionFlagType.WORKSPACE,
|
||||
},
|
||||
},
|
||||
] as RoleEntity['rolePermissionFlags'],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(roleDto.permissionFlags).toEqual([
|
||||
{
|
||||
id: 'role-permission-flag-id',
|
||||
roleId: 'role-id',
|
||||
flag: PermissionFlagType.WORKSPACE,
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
+8
-1
@@ -1,3 +1,5 @@
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
|
||||
import { type RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { type RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
|
||||
@@ -19,7 +21,12 @@ export const fromRoleEntityToRoleDto = (role: RoleEntity): RoleDTO => {
|
||||
canBeAssignedToAgents: role.canBeAssignedToAgents,
|
||||
canBeAssignedToApiKeys: role.canBeAssignedToApiKeys,
|
||||
roleTargets: role.roleTargets,
|
||||
permissionFlags: role.permissionFlags,
|
||||
permissionFlags: role.rolePermissionFlags?.map((rolePermissionFlag) => ({
|
||||
id: rolePermissionFlag.id,
|
||||
roleId: rolePermissionFlag.roleId,
|
||||
flag: (rolePermissionFlag.permissionFlag?.key ??
|
||||
rolePermissionFlag.flag) as PermissionFlagType,
|
||||
})),
|
||||
objectPermissions: role.objectPermissions,
|
||||
fieldPermissions: role.fieldPermissions,
|
||||
};
|
||||
|
||||
@@ -108,7 +108,9 @@ export class UserRoleService {
|
||||
},
|
||||
relations: {
|
||||
role: {
|
||||
permissionFlags: true,
|
||||
rolePermissionFlags: {
|
||||
permissionFlag: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
@@ -35,7 +35,8 @@ export const WORKSPACE_CACHE_KEYS_V2 = {
|
||||
flatApplicationMaps: 'flat-maps:flatApplicationMaps',
|
||||
flatRoleMaps: 'flat-maps:role',
|
||||
flatRoleTargetMaps: 'flat-maps:role-target',
|
||||
flatPermissionFlagMaps: 'flat-maps:permission-flag',
|
||||
flatRolePermissionFlagMaps: 'flat-maps:role-permission-flag',
|
||||
flatPermissionFlagMaps: 'flat-maps:permission-flag-catalog',
|
||||
flatObjectPermissionMaps: 'flat-maps:object-permission',
|
||||
flatFieldPermissionMaps: 'flat-maps:field-permission',
|
||||
ORMEntityMetadatas: 'orm:entity-metadatas',
|
||||
|
||||
+1
@@ -10,6 +10,7 @@ export const TWENTY_STANDARD_ALL_METADATA_NAME = [
|
||||
'viewGroup',
|
||||
'view',
|
||||
'navigationMenuItem',
|
||||
'permissionFlag',
|
||||
'role',
|
||||
'agent',
|
||||
'skill',
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
import { v4 } from 'uuid';
|
||||
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { addFlatEntityToFlatEntityMapsOrThrow } from 'src/engine/metadata-modules/flat-entity/utils/add-flat-entity-to-flat-entity-maps-or-throw.util';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { STANDARD_PERMISSION_FLAG_DEFINITIONS } from 'src/engine/metadata-modules/permission-flag/constants/standard-permission-flag-definitions.constant';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
|
||||
export const buildStandardFlatPermissionFlagMetadataMaps = ({
|
||||
now,
|
||||
workspaceId,
|
||||
twentyStandardApplicationId,
|
||||
}: {
|
||||
now: string;
|
||||
workspaceId: string;
|
||||
twentyStandardApplicationId: string;
|
||||
}): FlatEntityMaps<FlatPermissionFlag> => {
|
||||
let flatPermissionFlagMaps = createEmptyFlatEntityMaps();
|
||||
|
||||
for (const permissionFlagDefinition of STANDARD_PERMISSION_FLAG_DEFINITIONS) {
|
||||
flatPermissionFlagMaps = addFlatEntityToFlatEntityMapsOrThrow({
|
||||
flatEntity: {
|
||||
id: v4(),
|
||||
...permissionFlagDefinition,
|
||||
workspaceId,
|
||||
applicationId: twentyStandardApplicationId,
|
||||
universalIdentifier: permissionFlagDefinition.universalIdentifier,
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
rolePermissionFlagIds: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
},
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
}
|
||||
|
||||
return flatPermissionFlagMaps;
|
||||
};
|
||||
+2
-2
@@ -72,8 +72,8 @@ export const createStandardRoleFlatMetadata = ({
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
permissionFlagIds: [],
|
||||
permissionFlagUniversalIdentifiers: [],
|
||||
rolePermissionFlagIds: [],
|
||||
rolePermissionFlagUniversalIdentifiers: [],
|
||||
objectPermissionUniversalIdentifiers: [],
|
||||
fieldPermissionIds: [],
|
||||
fieldPermissionUniversalIdentifiers: [],
|
||||
|
||||
+8
@@ -15,6 +15,7 @@ import { buildStandardFlatObjectMetadataMaps } from 'src/engine/workspace-manage
|
||||
import { buildStandardFlatPageLayoutTabMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/page-layout-tab/build-standard-flat-page-layout-tab-metadata-maps.util';
|
||||
import { buildStandardFlatPageLayoutWidgetMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/page-layout-widget/build-standard-flat-page-layout-widget-metadata-maps.util';
|
||||
import { buildStandardFlatPageLayoutMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/page-layout/build-standard-flat-page-layout-metadata-maps.util';
|
||||
import { buildStandardFlatPermissionFlagMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/permission-flag/build-standard-flat-permission-flag-metadata-maps.util';
|
||||
import { buildStandardFlatRoleMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/role-metadata/build-standard-flat-role-metadata-maps.util';
|
||||
import { buildStandardFlatSkillMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/skill-metadata/build-standard-flat-skill-metadata-maps.util';
|
||||
import { buildStandardFlatViewFieldMetadataMaps } from 'src/engine/workspace-manager/twenty-standard-application/utils/view-field/build-standard-flat-view-field-metadata-maps.util';
|
||||
@@ -140,6 +141,12 @@ export const computeTwentyStandardApplicationAllFlatEntityMaps = ({
|
||||
dependencyFlatEntityMaps: undefined,
|
||||
});
|
||||
|
||||
const flatPermissionFlagMaps = buildStandardFlatPermissionFlagMetadataMaps({
|
||||
now,
|
||||
workspaceId,
|
||||
twentyStandardApplicationId,
|
||||
});
|
||||
|
||||
const flatAgentMaps = buildStandardFlatAgentMetadataMaps({
|
||||
now,
|
||||
workspaceId,
|
||||
@@ -213,6 +220,7 @@ export const computeTwentyStandardApplicationAllFlatEntityMaps = ({
|
||||
flatFieldMetadataMaps,
|
||||
flatObjectMetadataMaps,
|
||||
flatNavigationMenuItemMaps,
|
||||
flatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
flatAgentMaps,
|
||||
flatSkillMaps,
|
||||
|
||||
+1
@@ -167,6 +167,7 @@ export const enrichCreateWorkspaceMigrationActionsWithIds = ({
|
||||
case 'navigationMenuItem':
|
||||
case 'frontComponent':
|
||||
case 'viewSort':
|
||||
case 'rolePermissionFlag':
|
||||
case 'permissionFlag':
|
||||
case 'objectPermission':
|
||||
case 'fieldPermission':
|
||||
|
||||
+37
@@ -29,6 +29,7 @@ import { WorkspaceMigrationPageLayoutTabActionsBuilderService } from 'src/engine
|
||||
import { WorkspaceMigrationPageLayoutWidgetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout-widget/workspace-migration-page-layout-widget-actions-builder.service';
|
||||
import { WorkspaceMigrationPageLayoutActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/page-layout/workspace-migration-page-layout-actions-builder.service';
|
||||
import { WorkspaceMigrationPermissionFlagActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/workspace-migration-permission-flag-actions-builder.service';
|
||||
import { WorkspaceMigrationRolePermissionFlagActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-permission-flag/workspace-migration-role-permission-flag-actions-builder.service';
|
||||
import { WorkspaceMigrationRoleTargetActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-target/workspace-migration-role-target-actions-builder.service';
|
||||
import { WorkspaceMigrationRoleActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role/workspace-migration-role-actions-builder.service';
|
||||
import { WorkspaceMigrationRowLevelPermissionPredicateGroupActionsBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/row-level-permission-predicate-group/workspace-migration-row-level-permission-predicate-group-actions-builder.service';
|
||||
@@ -58,6 +59,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
private readonly workspaceMigrationViewSortActionsBuilderService: WorkspaceMigrationViewSortActionsBuilderService,
|
||||
private readonly workspaceMigrationFieldPermissionActionsBuilderService: WorkspaceMigrationFieldPermissionActionsBuilderService,
|
||||
private readonly workspaceMigrationObjectPermissionActionsBuilderService: WorkspaceMigrationObjectPermissionActionsBuilderService,
|
||||
private readonly workspaceMigrationRolePermissionFlagActionsBuilderService: WorkspaceMigrationRolePermissionFlagActionsBuilderService,
|
||||
private readonly workspaceMigrationPermissionFlagActionsBuilderService: WorkspaceMigrationPermissionFlagActionsBuilderService,
|
||||
private readonly workspaceMigrationLogicFunctionActionsBuilderService: WorkspaceMigrationLogicFunctionActionsBuilderService,
|
||||
private readonly workspaceMigrationRoleTargetActionsBuilderService: WorkspaceMigrationRoleTargetActionsBuilderService,
|
||||
@@ -158,6 +160,7 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
flatRoleMaps,
|
||||
flatObjectPermissionMaps,
|
||||
flatFieldPermissionMaps,
|
||||
flatRolePermissionFlagMaps,
|
||||
flatPermissionFlagMaps,
|
||||
flatRoleTargetMaps,
|
||||
flatAgentMaps,
|
||||
@@ -558,6 +561,34 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatRolePermissionFlagMaps)) {
|
||||
const {
|
||||
from: fromFlatRolePermissionFlagMaps,
|
||||
to: toFlatRolePermissionFlagMaps,
|
||||
} = flatRolePermissionFlagMaps;
|
||||
|
||||
const rolePermissionFlagResult =
|
||||
await this.workspaceMigrationRolePermissionFlagActionsBuilderService.validateAndBuild(
|
||||
{
|
||||
additionalCacheDataMaps,
|
||||
from: fromFlatRolePermissionFlagMaps,
|
||||
to: toFlatRolePermissionFlagMaps,
|
||||
buildOptions,
|
||||
dependencyOptimisticFlatEntityMaps: optimisticAllFlatEntityMaps,
|
||||
workspaceId,
|
||||
},
|
||||
);
|
||||
|
||||
if (rolePermissionFlagResult.status === 'fail') {
|
||||
orchestratorFailureReport.rolePermissionFlag.push(
|
||||
...rolePermissionFlagResult.errors,
|
||||
);
|
||||
} else {
|
||||
orchestratorActionsReport.rolePermissionFlag =
|
||||
rolePermissionFlagResult.actions;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatPermissionFlagMaps)) {
|
||||
const { from: fromFlatPermissionFlagMaps, to: toFlatPermissionFlagMaps } =
|
||||
flatPermissionFlagMaps;
|
||||
@@ -986,6 +1017,12 @@ export class WorkspaceMigrationBuildOrchestratorService {
|
||||
///
|
||||
|
||||
// Permission flags
|
||||
...aggregatedOrchestratorActionsReport.rolePermissionFlag.delete,
|
||||
...aggregatedOrchestratorActionsReport.rolePermissionFlag.create,
|
||||
...aggregatedOrchestratorActionsReport.rolePermissionFlag.update,
|
||||
///
|
||||
|
||||
// Permission flag definitions
|
||||
...aggregatedOrchestratorActionsReport.permissionFlag.delete,
|
||||
...aggregatedOrchestratorActionsReport.permissionFlag.create,
|
||||
...aggregatedOrchestratorActionsReport.permissionFlag.update,
|
||||
|
||||
+5
-2
@@ -35,15 +35,18 @@ exports[`ALL_UNIVERSAL_FLAT_ENTITY_FOREIGN_KEY_AGGREGATOR_PROPERTIES should matc
|
||||
"widgetUniversalIdentifiers",
|
||||
],
|
||||
"pageLayoutWidget": [],
|
||||
"permissionFlag": [],
|
||||
"permissionFlag": [
|
||||
"rolePermissionFlagUniversalIdentifiers",
|
||||
],
|
||||
"role": [
|
||||
"roleTargetUniversalIdentifiers",
|
||||
"objectPermissionUniversalIdentifiers",
|
||||
"permissionFlagUniversalIdentifiers",
|
||||
"rolePermissionFlagUniversalIdentifiers",
|
||||
"fieldPermissionUniversalIdentifiers",
|
||||
"rowLevelPermissionPredicateUniversalIdentifiers",
|
||||
"rowLevelPermissionPredicateGroupUniversalIdentifiers",
|
||||
],
|
||||
"rolePermissionFlag": [],
|
||||
"roleTarget": [],
|
||||
"rowLevelPermissionPredicate": [],
|
||||
"rowLevelPermissionPredicateGroup": [
|
||||
|
||||
+1
@@ -38,6 +38,7 @@ export const ALL_JSONB_PROPERTIES_WITH_SERIALIZED_RELATION_BY_METADATA_NAME = {
|
||||
},
|
||||
commandMenuItem: {},
|
||||
navigationMenuItem: {},
|
||||
rolePermissionFlag: {},
|
||||
permissionFlag: {},
|
||||
objectPermission: {},
|
||||
fieldPermission: {},
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
import { type RolePermissionFlagEntity } from 'src/engine/metadata-modules/role-permission-flag/role-permission-flag.entity';
|
||||
import { type UniversalFlatEntityFrom } from 'src/engine/workspace-manager/workspace-migration/universal-flat-entity/types/universal-flat-entity-from.type';
|
||||
|
||||
export type UniversalFlatRolePermissionFlag = UniversalFlatEntityFrom<
|
||||
RolePermissionFlagEntity,
|
||||
'rolePermissionFlag'
|
||||
>;
|
||||
+6
-6
@@ -2,12 +2,12 @@ import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
|
||||
import { type UniversalUpdatePermissionFlagAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
|
||||
import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
import { type UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
import { type UniversalFlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-result.type';
|
||||
import { WorkspaceEntityMigrationBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/services/workspace-entity-migration-builder.service';
|
||||
import { UniversalUpdatePermissionFlagAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/permission-flag/types/workspace-migration-permission-flag-action.type';
|
||||
import { FlatPermissionFlagValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-permission-flag-validator.service';
|
||||
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
import { UniversalFlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-result.type';
|
||||
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationPermissionFlagActionsBuilderService extends WorkspaceEntityMigrationBuilderService<
|
||||
@@ -105,7 +105,7 @@ export class WorkspaceMigrationPermissionFlagActionsBuilderService extends Works
|
||||
|
||||
const { universalIdentifier, flatEntityUpdate } = args;
|
||||
|
||||
const updatePermissionFlagAction: UniversalUpdatePermissionFlagAction = {
|
||||
const updateAction: UniversalUpdatePermissionFlagAction = {
|
||||
type: 'update',
|
||||
metadataName: 'permissionFlag',
|
||||
universalIdentifier,
|
||||
@@ -114,7 +114,7 @@ export class WorkspaceMigrationPermissionFlagActionsBuilderService extends Works
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: updatePermissionFlagAction,
|
||||
action: updateAction,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
import { type BaseFlatCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-create-workspace-migration-action.type';
|
||||
import { type BaseFlatDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-delete-workspace-migration-action.type';
|
||||
import { type BaseFlatUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-flat-update-workspace-migration-action.type';
|
||||
import { type BaseUniversalCreateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-create-workspace-migration-action.type';
|
||||
import { type BaseUniversalDeleteWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-delete-workspace-migration-action.type';
|
||||
import { type BaseUniversalUpdateWorkspaceMigrationAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/base-universal-update-workspace-migration-action.type';
|
||||
|
||||
export type FlatCreateRolePermissionFlagAction =
|
||||
BaseFlatCreateWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
|
||||
export type FlatUpdateRolePermissionFlagAction =
|
||||
BaseFlatUpdateWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
|
||||
export type FlatDeleteRolePermissionFlagAction =
|
||||
BaseFlatDeleteWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
|
||||
export type UniversalCreateRolePermissionFlagAction =
|
||||
BaseUniversalCreateWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
|
||||
export type UniversalUpdateRolePermissionFlagAction =
|
||||
BaseUniversalUpdateWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
|
||||
export type UniversalDeleteRolePermissionFlagAction =
|
||||
BaseUniversalDeleteWorkspaceMigrationAction<'rolePermissionFlag'>;
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
|
||||
import { WorkspaceEntityMigrationBuilderService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/services/workspace-entity-migration-builder.service';
|
||||
import { UniversalUpdateRolePermissionFlagAction } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/role-permission-flag/types/workspace-migration-role-permission-flag-action.type';
|
||||
import { FlatRolePermissionFlagValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-role-permission-flag-validator.service';
|
||||
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
import { UniversalFlatEntityValidationReturnType } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-result.type';
|
||||
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class WorkspaceMigrationRolePermissionFlagActionsBuilderService extends WorkspaceEntityMigrationBuilderService<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
> {
|
||||
constructor(
|
||||
private readonly flatRolePermissionFlagValidatorService: FlatRolePermissionFlagValidatorService,
|
||||
) {
|
||||
super(ALL_METADATA_NAME.rolePermissionFlag);
|
||||
}
|
||||
|
||||
protected validateFlatEntityCreation(
|
||||
args: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>,
|
||||
): UniversalFlatEntityValidationReturnType<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag,
|
||||
'create'
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatRolePermissionFlagValidatorService.validateFlatRolePermissionFlagCreation(
|
||||
args,
|
||||
);
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const { flatEntityToValidate: flatRolePermissionFlagToValidate } = args;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'create',
|
||||
metadataName: 'rolePermissionFlag',
|
||||
flatEntity: flatRolePermissionFlagToValidate,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected validateFlatEntityDeletion(
|
||||
args: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>,
|
||||
): UniversalFlatEntityValidationReturnType<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag,
|
||||
'delete'
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatRolePermissionFlagValidatorService.validateFlatRolePermissionFlagDeletion(
|
||||
args,
|
||||
);
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const { flatEntityToValidate: flatRolePermissionFlagToValidate } = args;
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: {
|
||||
type: 'delete',
|
||||
metadataName: 'rolePermissionFlag',
|
||||
universalIdentifier:
|
||||
flatRolePermissionFlagToValidate.universalIdentifier,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
protected validateFlatEntityUpdate(
|
||||
args: FlatEntityUpdateValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>,
|
||||
): UniversalFlatEntityValidationReturnType<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag,
|
||||
'update'
|
||||
> {
|
||||
const validationResult =
|
||||
this.flatRolePermissionFlagValidatorService.validateFlatRolePermissionFlagUpdate(
|
||||
args,
|
||||
);
|
||||
|
||||
if (validationResult.errors.length > 0) {
|
||||
return {
|
||||
status: 'fail',
|
||||
...validationResult,
|
||||
};
|
||||
}
|
||||
|
||||
const { universalIdentifier, flatEntityUpdate } = args;
|
||||
|
||||
const updateRolePermissionFlagAction: UniversalUpdateRolePermissionFlagAction =
|
||||
{
|
||||
type: 'update',
|
||||
metadataName: 'rolePermissionFlag',
|
||||
universalIdentifier,
|
||||
update: flatEntityUpdate,
|
||||
};
|
||||
|
||||
return {
|
||||
status: 'success',
|
||||
action: updateRolePermissionFlagAction,
|
||||
};
|
||||
}
|
||||
}
|
||||
+381
@@ -0,0 +1,381 @@
|
||||
import { Test, type TestingModule } from '@nestjs/testing';
|
||||
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
|
||||
import { createEmptyFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/constant/create-empty-flat-entity-maps.constant';
|
||||
import { type FlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/types/flat-entity-maps.type';
|
||||
import { type FlatRolePermissionFlag } from 'src/engine/metadata-modules/flat-role-permission-flag/types/flat-role-permission-flag.type';
|
||||
import { type FlatPermissionFlag } from 'src/engine/metadata-modules/flat-permission-flag/types/flat-permission-flag.type';
|
||||
import { type PermissionFlagPermissionType } from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
import { PermissionFlagExceptionCode } from 'src/engine/metadata-modules/permission-flag/permission-flag.exception';
|
||||
import { TWENTY_STANDARD_APPLICATION } from 'src/engine/workspace-manager/twenty-standard-application/constants/twenty-standard-applications';
|
||||
import { FlatPermissionFlagValidatorService } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/services/flat-permission-flag-validator.service';
|
||||
|
||||
const buildFlatDefinition = (
|
||||
overrides: Partial<FlatPermissionFlag> = {},
|
||||
): FlatPermissionFlag =>
|
||||
({
|
||||
id: '00000000-0000-0000-0000-000000000001',
|
||||
universalIdentifier: '00000000-0000-0000-0000-000000000001',
|
||||
key: 'TEST_FLAG',
|
||||
label: 'Test Flag',
|
||||
description: 'A flag for tests',
|
||||
icon: 'IconTest',
|
||||
permissionType: 'tool',
|
||||
workspaceId: 'workspace-id',
|
||||
applicationId: '00000000-0000-0000-0000-000000000aaa',
|
||||
applicationUniversalIdentifier: '00000000-0000-0000-0000-000000000aaa',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
...overrides,
|
||||
}) as unknown as FlatPermissionFlag;
|
||||
|
||||
const buildEmptyMaps = (): FlatEntityMaps<FlatPermissionFlag> =>
|
||||
createEmptyFlatEntityMaps() as unknown as FlatEntityMaps<FlatPermissionFlag>;
|
||||
|
||||
const buildEmptyRolePermissionFlagMaps =
|
||||
(): FlatEntityMaps<FlatRolePermissionFlag> =>
|
||||
createEmptyFlatEntityMaps() as unknown as FlatEntityMaps<FlatRolePermissionFlag>;
|
||||
|
||||
const buildFlatRolePermissionFlag = (
|
||||
overrides: Partial<FlatRolePermissionFlag> = {},
|
||||
): FlatRolePermissionFlag =>
|
||||
({
|
||||
id: '00000000-0000-0000-0000-000000000101',
|
||||
universalIdentifier: '00000000-0000-0000-0000-000000000101',
|
||||
permissionFlagId: '00000000-0000-0000-0000-000000000001',
|
||||
permissionFlagUniversalIdentifier: '00000000-0000-0000-0000-000000000001',
|
||||
roleUniversalIdentifier: '00000000-0000-0000-0000-000000000201',
|
||||
workspaceId: 'workspace-id',
|
||||
applicationId: '00000000-0000-0000-0000-000000000aaa',
|
||||
applicationUniversalIdentifier: '00000000-0000-0000-0000-000000000aaa',
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: new Date().toISOString(),
|
||||
...overrides,
|
||||
}) as unknown as FlatRolePermissionFlag;
|
||||
|
||||
const buildArgs = (
|
||||
flatEntityToValidate: FlatPermissionFlag,
|
||||
optimisticMaps: FlatEntityMaps<FlatPermissionFlag> = buildEmptyMaps(),
|
||||
) =>
|
||||
({
|
||||
flatEntityToValidate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
}) as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagCreation']
|
||||
>[0];
|
||||
|
||||
describe('FlatPermissionFlagValidatorService', () => {
|
||||
let service: FlatPermissionFlagValidatorService;
|
||||
|
||||
beforeEach(async () => {
|
||||
const moduleRef: TestingModule = await Test.createTestingModule({
|
||||
providers: [FlatPermissionFlagValidatorService],
|
||||
}).compile();
|
||||
|
||||
service = moduleRef.get(FlatPermissionFlagValidatorService);
|
||||
});
|
||||
|
||||
describe('validateFlatPermissionFlagCreation', () => {
|
||||
it('passes for a valid definition', () => {
|
||||
const result = service.validateFlatPermissionFlagCreation(
|
||||
buildArgs(buildFlatDefinition()),
|
||||
);
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('rejects an empty key', () => {
|
||||
const result = service.validateFlatPermissionFlagCreation(
|
||||
buildArgs(buildFlatDefinition({ key: '' })),
|
||||
);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_KEY,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects an unknown permission type', () => {
|
||||
const result = service.validateFlatPermissionFlagCreation(
|
||||
buildArgs(
|
||||
buildFlatDefinition({
|
||||
permissionType: 'invalid' as PermissionFlagPermissionType,
|
||||
}),
|
||||
),
|
||||
);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_PERMISSION_TYPE,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects a duplicate key in the same workspace', () => {
|
||||
const existing = buildFlatDefinition({
|
||||
universalIdentifier: '00000000-0000-0000-0000-000000000999',
|
||||
key: 'EXISTING_KEY',
|
||||
});
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagCreation(
|
||||
buildArgs(buildFlatDefinition({ key: 'EXISTING_KEY' }), optimisticMaps),
|
||||
);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_ALREADY_EXISTS,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects a duplicate universal identifier', () => {
|
||||
const existing = buildFlatDefinition();
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagCreation(
|
||||
buildArgs(
|
||||
buildFlatDefinition({
|
||||
id: '00000000-0000-0000-0000-000000000002',
|
||||
key: 'ANOTHER_TEST_FLAG',
|
||||
}),
|
||||
optimisticMaps,
|
||||
),
|
||||
);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_ALREADY_EXISTS,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateFlatPermissionFlagUpdate', () => {
|
||||
it('passes for a valid update', () => {
|
||||
const existing = buildFlatDefinition();
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagUpdate({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
flatEntityUpdate: { label: 'Updated Label' },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagUpdate']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('rejects updating the immutable key field', () => {
|
||||
const existing = buildFlatDefinition({ key: 'ORIGINAL_KEY' });
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagUpdate({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
flatEntityUpdate: { key: 'NEW_KEY' },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagUpdate']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_KEY_IMMUTABLE,
|
||||
]);
|
||||
});
|
||||
|
||||
it('returns not-found if no existing definition matches the universalIdentifier', () => {
|
||||
const result = service.validateFlatPermissionFlagUpdate({
|
||||
universalIdentifier: '00000000-0000-0000-0000-deadbeefdead',
|
||||
flatEntityUpdate: { label: 'New Label' },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: buildEmptyMaps(),
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagUpdate']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects updating a standard definition from a custom application', () => {
|
||||
const existing = buildFlatDefinition({
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
});
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagUpdate({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
flatEntityUpdate: { label: 'Updated Label' },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
'00000000-0000-0000-0000-000000000aaa',
|
||||
},
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagUpdate']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_IS_STANDARD,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects updating to an unknown permission type', () => {
|
||||
const existing = buildFlatDefinition();
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagUpdate({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
flatEntityUpdate: {
|
||||
permissionType: 'invalid' as PermissionFlagPermissionType,
|
||||
},
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagUpdate']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_PERMISSION_TYPE,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('validateFlatPermissionFlagDeletion', () => {
|
||||
it('passes for a valid deletion', () => {
|
||||
const existing = buildFlatDefinition();
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagDeletion({
|
||||
flatEntityToValidate: existing,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
flatRolePermissionFlagMaps: buildEmptyRolePermissionFlagMaps(),
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagDeletion']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('returns not-found if no existing definition matches the universalIdentifier', () => {
|
||||
const result = service.validateFlatPermissionFlagDeletion({
|
||||
flatEntityToValidate: buildFlatDefinition({
|
||||
universalIdentifier: '00000000-0000-0000-0000-deadbeefdead',
|
||||
}),
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: buildEmptyMaps(),
|
||||
flatRolePermissionFlagMaps: buildEmptyRolePermissionFlagMaps(),
|
||||
},
|
||||
buildOptions: {} as never,
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagDeletion']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects deleting a standard definition from a custom application', () => {
|
||||
const existing = buildFlatDefinition({
|
||||
applicationUniversalIdentifier:
|
||||
TWENTY_STANDARD_APPLICATION.universalIdentifier,
|
||||
});
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const result = service.validateFlatPermissionFlagDeletion({
|
||||
flatEntityToValidate: existing,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
flatRolePermissionFlagMaps: buildEmptyRolePermissionFlagMaps(),
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
'00000000-0000-0000-0000-000000000aaa',
|
||||
},
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagDeletion']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_IS_STANDARD,
|
||||
]);
|
||||
});
|
||||
|
||||
it('rejects deleting a definition while roles still grant its key', () => {
|
||||
const existing = buildFlatDefinition({
|
||||
key: PermissionFlagType.WORKSPACE,
|
||||
});
|
||||
const optimisticMaps = buildEmptyMaps();
|
||||
optimisticMaps.byUniversalIdentifier[existing.universalIdentifier] =
|
||||
existing;
|
||||
|
||||
const rolePermissionFlagMaps = buildEmptyRolePermissionFlagMaps();
|
||||
const rolePermissionFlag = buildFlatRolePermissionFlag({
|
||||
permissionFlagId: existing.id,
|
||||
permissionFlagUniversalIdentifier: existing.universalIdentifier,
|
||||
});
|
||||
rolePermissionFlagMaps.byUniversalIdentifier[
|
||||
rolePermissionFlag.universalIdentifier
|
||||
] = rolePermissionFlag;
|
||||
|
||||
const result = service.validateFlatPermissionFlagDeletion({
|
||||
flatEntityToValidate: existing,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticMaps,
|
||||
flatRolePermissionFlagMaps: rolePermissionFlagMaps,
|
||||
},
|
||||
buildOptions: {
|
||||
isSystemBuild: false,
|
||||
applicationUniversalIdentifier:
|
||||
'00000000-0000-0000-0000-000000000aaa',
|
||||
},
|
||||
} as unknown as Parameters<
|
||||
FlatPermissionFlagValidatorService['validateFlatPermissionFlagDeletion']
|
||||
>[0]);
|
||||
|
||||
expect(result.errors.map((error) => error.code)).toEqual([
|
||||
PermissionFlagExceptionCode.PERMISSION_FLAG_IN_USE,
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
// ALL_METADATA_NAME is referenced as a sanity check the metadata name exists
|
||||
it('uses the registered ALL_METADATA_NAME entry', () => {
|
||||
expect(ALL_METADATA_NAME.permissionFlag).toBe('permissionFlag');
|
||||
});
|
||||
});
|
||||
+115
-148
@@ -1,17 +1,19 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { isNonEmptyString } from '@sniptt/guards';
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { PermissionsExceptionCode } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
import { PERMISSION_FLAG_PERMISSION_TYPES } from 'src/engine/metadata-modules/permission-flag/constants/permission-flag-permission-type.constant';
|
||||
import { PermissionFlagExceptionCode } from 'src/engine/metadata-modules/permission-flag/permission-flag.exception';
|
||||
import { belongsToTwentyStandardApp } from 'src/engine/metadata-modules/utils/belongs-to-twenty-standard-app.util';
|
||||
import { isCallerTwentyStandardApp } from 'src/engine/metadata-modules/utils/is-caller-twenty-standard-app.util';
|
||||
import { type FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
import { getEmptyFlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/utils/get-flat-entity-validation-error.util';
|
||||
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
import { validateRoleBelongsToCallerApplication } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/utils/validate-role-belongs-to-caller-application.util';
|
||||
import { type FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
import { type UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
|
||||
@Injectable()
|
||||
export class FlatPermissionFlagValidatorService {
|
||||
@@ -19,17 +21,14 @@ export class FlatPermissionFlagValidatorService {
|
||||
flatEntityToValidate: flatPermissionFlagToValidate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.permissionFlag
|
||||
>): FailedFlatEntityValidation<'permissionFlag', 'create'> {
|
||||
const validationResult = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier: flatPermissionFlagToValidate.universalIdentifier,
|
||||
roleUniversalIdentifier:
|
||||
flatPermissionFlagToValidate.roleUniversalIdentifier,
|
||||
key: flatPermissionFlagToValidate.key,
|
||||
},
|
||||
metadataName: 'permissionFlag',
|
||||
type: 'create',
|
||||
@@ -42,71 +41,47 @@ export class FlatPermissionFlagValidatorService {
|
||||
|
||||
if (isDefined(existingByUniversalId)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag with universal identifier ${flatPermissionFlagToValidate.universalIdentifier} already exists`,
|
||||
userFriendlyMessage: msg`Permission flag already exists`,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_ALREADY_EXISTS,
|
||||
message: t`Permission flag definition with universal identifier ${flatPermissionFlagToValidate.universalIdentifier} already exists`,
|
||||
userFriendlyMessage: msg`Permission flag definition already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier: flatPermissionFlagToValidate.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedRole)) {
|
||||
if (!isNonEmptyString(flatPermissionFlagToValidate.key)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
message: t`Role not found`,
|
||||
userFriendlyMessage: msg`Role not found`,
|
||||
});
|
||||
} else {
|
||||
validationResult.errors.push(
|
||||
...validateRoleBelongsToCallerApplication({
|
||||
referencedRole,
|
||||
buildOptions,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const isValidFlag =
|
||||
isDefined(flatPermissionFlagToValidate.flag) &&
|
||||
Object.values(PermissionFlagType).includes(
|
||||
flatPermissionFlagToValidate.flag,
|
||||
);
|
||||
|
||||
if (!isValidFlag) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Invalid permission flag value`,
|
||||
userFriendlyMessage: msg`Invalid permission setting`,
|
||||
code: PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_KEY,
|
||||
message: t`Permission flag definition key is required`,
|
||||
userFriendlyMessage: msg`Key is required`,
|
||||
});
|
||||
}
|
||||
|
||||
const duplicateForSameRole = Object.values(
|
||||
const duplicateKey = Object.values(
|
||||
optimisticFlatPermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf) =>
|
||||
isDefined(pf) &&
|
||||
pf.roleUniversalIdentifier ===
|
||||
flatPermissionFlagToValidate.roleUniversalIdentifier &&
|
||||
pf.flag === flatPermissionFlagToValidate.flag &&
|
||||
pf.universalIdentifier !==
|
||||
(definition) =>
|
||||
isDefined(definition) &&
|
||||
definition.key === flatPermissionFlagToValidate.key &&
|
||||
definition.universalIdentifier !==
|
||||
flatPermissionFlagToValidate.universalIdentifier,
|
||||
);
|
||||
|
||||
if (duplicateForSameRole.length > 0) {
|
||||
if (duplicateKey.length > 0) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag for this role and setting already exists`,
|
||||
userFriendlyMessage: msg`This permission is already set for the role`,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_ALREADY_EXISTS,
|
||||
message: t`Permission flag definition with key ${flatPermissionFlagToValidate.key} already exists in this workspace`,
|
||||
userFriendlyMessage: msg`A permission flag with this key already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
!PERMISSION_FLAG_PERMISSION_TYPES.includes(
|
||||
flatPermissionFlagToValidate.permissionType,
|
||||
)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_PERMISSION_TYPE,
|
||||
message: t`Permission flag definition permission type must be 'settings' or 'tool'`,
|
||||
userFriendlyMessage: msg`Invalid permission type`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -118,17 +93,11 @@ export class FlatPermissionFlagValidatorService {
|
||||
flatEntityUpdate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
typeof ALL_METADATA_NAME.permissionFlag
|
||||
>): FailedFlatEntityValidation<'permissionFlag', 'update'> {
|
||||
const existingFlatPermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
const validationResult = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier,
|
||||
@@ -137,79 +106,56 @@ export class FlatPermissionFlagValidatorService {
|
||||
type: 'update',
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatPermissionFlag)) {
|
||||
const existing = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existing)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
|
||||
message: t`Permission flag to update not found`,
|
||||
userFriendlyMessage: msg`Permission flag not found`,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
message: t`Permission flag definition to update not found`,
|
||||
userFriendlyMessage: msg`Permission flag definition not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const updatedFlatPermissionFlag = {
|
||||
...existingFlatPermissionFlag,
|
||||
...flatEntityUpdate,
|
||||
};
|
||||
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier: updatedFlatPermissionFlag.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedRole)) {
|
||||
if (
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
applicationUniversalIdentifier: existing.applicationUniversalIdentifier,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
message: t`Role not found`,
|
||||
userFriendlyMessage: msg`Role not found`,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_IS_STANDARD,
|
||||
message: t`Cannot update standard permission flag definition`,
|
||||
userFriendlyMessage: msg`Cannot update standard permission flag definition`,
|
||||
});
|
||||
} else {
|
||||
validationResult.errors.push(
|
||||
...validateRoleBelongsToCallerApplication({
|
||||
referencedRole,
|
||||
buildOptions,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatEntityUpdate.flag)) {
|
||||
const isValidFlag = Object.values(PermissionFlagType).includes(
|
||||
flatEntityUpdate.flag as PermissionFlagType,
|
||||
);
|
||||
|
||||
if (!isValidFlag) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Invalid permission flag value`,
|
||||
userFriendlyMessage: msg`Invalid permission setting`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateForSameRole = Object.values(
|
||||
optimisticFlatPermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf) =>
|
||||
isDefined(pf) &&
|
||||
pf.roleUniversalIdentifier ===
|
||||
updatedFlatPermissionFlag.roleUniversalIdentifier &&
|
||||
pf.flag === updatedFlatPermissionFlag.flag &&
|
||||
pf.universalIdentifier !== universalIdentifier,
|
||||
);
|
||||
|
||||
if (duplicateForSameRole.length > 0) {
|
||||
if (
|
||||
isDefined(flatEntityUpdate.key) &&
|
||||
flatEntityUpdate.key !== existing.key
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag for this role and setting already exists`,
|
||||
userFriendlyMessage: msg`This permission is already set for the role`,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_KEY_IMMUTABLE,
|
||||
message: t`Permission flag definition key cannot be changed after creation`,
|
||||
userFriendlyMessage: msg`Key cannot be changed`,
|
||||
});
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(flatEntityUpdate.permissionType) &&
|
||||
!PERMISSION_FLAG_PERMISSION_TYPES.includes(
|
||||
flatEntityUpdate.permissionType,
|
||||
)
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionFlagExceptionCode.INVALID_PERMISSION_FLAG_PERMISSION_TYPE,
|
||||
message: t`Permission flag definition permission type must be 'settings' or 'tool'`,
|
||||
userFriendlyMessage: msg`Invalid permission type`,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -220,8 +166,9 @@ export class FlatPermissionFlagValidatorService {
|
||||
flatEntityToValidate: { universalIdentifier },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps: optimisticFlatPermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
flatRolePermissionFlagMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.permissionFlag
|
||||
>): FailedFlatEntityValidation<'permissionFlag', 'delete'> {
|
||||
@@ -233,30 +180,50 @@ export class FlatPermissionFlagValidatorService {
|
||||
type: 'delete',
|
||||
});
|
||||
|
||||
const existingFlatPermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
const existing = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatPermissionFlag)) {
|
||||
if (!isDefined(existing)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
|
||||
message: t`Permission flag to delete not found`,
|
||||
userFriendlyMessage: msg`Permission flag not found`,
|
||||
});
|
||||
} else {
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier: existingFlatPermissionFlag.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_NOT_FOUND,
|
||||
message: t`Permission flag definition to delete not found`,
|
||||
userFriendlyMessage: msg`Permission flag definition not found`,
|
||||
});
|
||||
|
||||
if (isDefined(referencedRole) && !referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
if (
|
||||
!isCallerTwentyStandardApp(buildOptions) &&
|
||||
belongsToTwentyStandardApp({
|
||||
universalIdentifier: existing.universalIdentifier,
|
||||
applicationUniversalIdentifier: existing.applicationUniversalIdentifier,
|
||||
})
|
||||
) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_IS_STANDARD,
|
||||
message: t`Cannot delete standard permission flag definition`,
|
||||
userFriendlyMessage: msg`Cannot delete standard permission flag definition`,
|
||||
});
|
||||
}
|
||||
|
||||
const isPermissionFlagInUse = Object.values(
|
||||
optimisticFlatRolePermissionFlagMaps.byUniversalIdentifier,
|
||||
).some(
|
||||
(rolePermissionFlag) =>
|
||||
isDefined(rolePermissionFlag) &&
|
||||
rolePermissionFlag.permissionFlagUniversalIdentifier ===
|
||||
existing.universalIdentifier,
|
||||
);
|
||||
|
||||
if (isPermissionFlagInUse) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionFlagExceptionCode.PERMISSION_FLAG_IN_USE,
|
||||
message: t`Permission flag definition with key ${existing.key} is still assigned to a role`,
|
||||
userFriendlyMessage: msg`Remove this permission from all roles before deleting it`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
|
||||
+281
@@ -0,0 +1,281 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
|
||||
import { msg, t } from '@lingui/core/macro';
|
||||
import { ALL_METADATA_NAME } from 'twenty-shared/metadata';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { findFlatEntityByUniversalIdentifier } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-universal-identifier.util';
|
||||
import { PermissionsExceptionCode } from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { FailedFlatEntityValidation } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/types/failed-flat-entity-validation.type';
|
||||
import { getEmptyFlatEntityValidationError } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/builders/utils/get-flat-entity-validation-error.util';
|
||||
import { FlatEntityUpdateValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-update-validation-args.type';
|
||||
import { UniversalFlatEntityValidationArgs } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/types/universal-flat-entity-validation-args.type';
|
||||
import { validateRoleBelongsToCallerApplication } from 'src/engine/workspace-manager/workspace-migration/workspace-migration-builder/validators/utils/validate-role-belongs-to-caller-application.util';
|
||||
|
||||
@Injectable()
|
||||
export class FlatRolePermissionFlagValidatorService {
|
||||
validateFlatRolePermissionFlagCreation({
|
||||
flatEntityToValidate: flatRolePermissionFlagToValidate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps,
|
||||
flatRolePermissionFlagMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>): FailedFlatEntityValidation<'rolePermissionFlag', 'create'> {
|
||||
const validationResult = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier:
|
||||
flatRolePermissionFlagToValidate.universalIdentifier,
|
||||
permissionFlagUniversalIdentifier:
|
||||
flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier,
|
||||
roleUniversalIdentifier:
|
||||
flatRolePermissionFlagToValidate.roleUniversalIdentifier,
|
||||
},
|
||||
metadataName: 'rolePermissionFlag',
|
||||
type: 'create',
|
||||
});
|
||||
|
||||
const existingByUniversalId = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier: flatRolePermissionFlagToValidate.universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (isDefined(existingByUniversalId)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Role permission flag with universal identifier ${flatRolePermissionFlagToValidate.universalIdentifier} already exists`,
|
||||
userFriendlyMessage: msg`Role permission flag already exists`,
|
||||
});
|
||||
}
|
||||
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatRolePermissionFlagToValidate.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedRole)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
message: t`Role not found`,
|
||||
userFriendlyMessage: msg`Role not found`,
|
||||
});
|
||||
} else {
|
||||
validationResult.errors.push(
|
||||
...validateRoleBelongsToCallerApplication({
|
||||
referencedRole,
|
||||
buildOptions,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
isDefined(
|
||||
flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier,
|
||||
)
|
||||
) {
|
||||
const referencedPermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier,
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedPermissionFlag)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag not found`,
|
||||
userFriendlyMessage: msg`Invalid permission setting`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateForSameRole = Object.values(
|
||||
optimisticFlatRolePermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf) =>
|
||||
isDefined(pf) &&
|
||||
pf.roleUniversalIdentifier ===
|
||||
flatRolePermissionFlagToValidate.roleUniversalIdentifier &&
|
||||
pf.permissionFlagUniversalIdentifier ===
|
||||
flatRolePermissionFlagToValidate.permissionFlagUniversalIdentifier &&
|
||||
pf.universalIdentifier !==
|
||||
flatRolePermissionFlagToValidate.universalIdentifier,
|
||||
);
|
||||
|
||||
if (duplicateForSameRole.length > 0) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag for this role and setting already exists`,
|
||||
userFriendlyMessage: msg`This permission is already set for the role`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validateFlatRolePermissionFlagUpdate({
|
||||
universalIdentifier,
|
||||
flatEntityUpdate,
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatPermissionFlagMaps,
|
||||
flatRolePermissionFlagMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
},
|
||||
buildOptions,
|
||||
}: FlatEntityUpdateValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>): FailedFlatEntityValidation<'rolePermissionFlag', 'update'> {
|
||||
const existingFlatRolePermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
});
|
||||
|
||||
const validationResult = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier,
|
||||
},
|
||||
metadataName: 'rolePermissionFlag',
|
||||
type: 'update',
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatRolePermissionFlag)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
|
||||
message: t`Permission flag to update not found`,
|
||||
userFriendlyMessage: msg`Permission flag not found`,
|
||||
});
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
const updatedFlatRolePermissionFlag = {
|
||||
...existingFlatRolePermissionFlag,
|
||||
...flatEntityUpdate,
|
||||
};
|
||||
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
updatedFlatRolePermissionFlag.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedRole)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_FOUND,
|
||||
message: t`Role not found`,
|
||||
userFriendlyMessage: msg`Role not found`,
|
||||
});
|
||||
} else {
|
||||
validationResult.errors.push(
|
||||
...validateRoleBelongsToCallerApplication({
|
||||
referencedRole,
|
||||
buildOptions,
|
||||
}),
|
||||
);
|
||||
|
||||
if (!referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (isDefined(flatEntityUpdate.permissionFlagUniversalIdentifier)) {
|
||||
const referencedPermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
flatEntityUpdate.permissionFlagUniversalIdentifier as string,
|
||||
flatEntityMaps: flatPermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(referencedPermissionFlag)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag not found`,
|
||||
userFriendlyMessage: msg`Invalid permission setting`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const duplicateForSameRole = Object.values(
|
||||
optimisticFlatRolePermissionFlagMaps.byUniversalIdentifier,
|
||||
).filter(
|
||||
(pf) =>
|
||||
isDefined(pf) &&
|
||||
pf.roleUniversalIdentifier ===
|
||||
updatedFlatRolePermissionFlag.roleUniversalIdentifier &&
|
||||
pf.permissionFlagUniversalIdentifier ===
|
||||
updatedFlatRolePermissionFlag.permissionFlagUniversalIdentifier &&
|
||||
pf.universalIdentifier !== universalIdentifier,
|
||||
);
|
||||
|
||||
if (duplicateForSameRole.length > 0) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.INVALID_SETTING,
|
||||
message: t`Permission flag for this role and setting already exists`,
|
||||
userFriendlyMessage: msg`This permission is already set for the role`,
|
||||
});
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
|
||||
validateFlatRolePermissionFlagDeletion({
|
||||
flatEntityToValidate: { universalIdentifier },
|
||||
optimisticFlatEntityMapsAndRelatedFlatEntityMaps: {
|
||||
flatRolePermissionFlagMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
flatRoleMaps,
|
||||
},
|
||||
}: UniversalFlatEntityValidationArgs<
|
||||
typeof ALL_METADATA_NAME.rolePermissionFlag
|
||||
>): FailedFlatEntityValidation<'rolePermissionFlag', 'delete'> {
|
||||
const validationResult = getEmptyFlatEntityValidationError({
|
||||
flatEntityMinimalInformation: {
|
||||
universalIdentifier,
|
||||
},
|
||||
metadataName: 'rolePermissionFlag',
|
||||
type: 'delete',
|
||||
});
|
||||
|
||||
const existingFlatRolePermissionFlag = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier,
|
||||
flatEntityMaps: optimisticFlatRolePermissionFlagMaps,
|
||||
});
|
||||
|
||||
if (!isDefined(existingFlatRolePermissionFlag)) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.PERMISSION_NOT_FOUND,
|
||||
message: t`Permission flag to delete not found`,
|
||||
userFriendlyMessage: msg`Permission flag not found`,
|
||||
});
|
||||
} else {
|
||||
const referencedRole = findFlatEntityByUniversalIdentifier({
|
||||
universalIdentifier:
|
||||
existingFlatRolePermissionFlag.roleUniversalIdentifier,
|
||||
flatEntityMaps: flatRoleMaps,
|
||||
});
|
||||
|
||||
if (isDefined(referencedRole) && !referencedRole.isEditable) {
|
||||
validationResult.errors.push({
|
||||
code: PermissionsExceptionCode.ROLE_NOT_EDITABLE,
|
||||
message: t`Role is not editable`,
|
||||
userFriendlyMessage: msg`This role cannot be modified because it is a system role. Only custom roles can be edited.`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return validationResult;
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user