[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:
+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: {
|
||||
|
||||
Reference in New Issue
Block a user