Rollout field permissions (#14024)
Rolling out field permissions feature
This commit is contained in:
-8
@@ -133,7 +133,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
|
||||
IS_WORKFLOW_BRANCH_ENABLED: false,
|
||||
IS_RELATION_CONNECT_ENABLED: false,
|
||||
IS_FIELDS_PERMISSIONS_ENABLED: false,
|
||||
IS_CORE_VIEW_SYNCING_ENABLED: false,
|
||||
IS_CORE_VIEW_ENABLED: false,
|
||||
IS_WORKSPACE_MIGRATION_V2_ENABLED: false,
|
||||
@@ -158,7 +157,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
IS_MORPH_RELATION_ENABLED: false,
|
||||
|
||||
IS_RELATION_CONNECT_ENABLED: false,
|
||||
IS_FIELDS_PERMISSIONS_ENABLED: true,
|
||||
IS_CORE_VIEW_SYNCING_ENABLED: false,
|
||||
},
|
||||
permissionsPerRoleId: {},
|
||||
@@ -235,10 +233,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
({ formattedResult }: { formattedResult: string[] }) => formattedResult,
|
||||
);
|
||||
|
||||
jest.spyOn(entityManager as any, 'getFeatureFlagMap').mockReturnValue({
|
||||
IS_FIELDS_PERMISSIONS_ENABLED: true,
|
||||
});
|
||||
|
||||
jest
|
||||
.spyOn(entityManager as any, 'extractTargetNameSingularFromEntityTarget')
|
||||
.mockImplementation((entityName: string) => {
|
||||
@@ -327,7 +321,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
});
|
||||
expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({
|
||||
entityName: 'test-entity',
|
||||
isFieldPermissionsEnabled: true,
|
||||
operationType: 'update',
|
||||
objectMetadataMaps: mockInternalContext.objectMetadataMaps,
|
||||
objectsPermissions: mockPermissionOptions.objectRecordsPermissions,
|
||||
@@ -362,7 +355,6 @@ describe('WorkspaceEntityManager', () => {
|
||||
expect(validateOperationIsPermittedOrThrow).toHaveBeenCalledWith({
|
||||
entityName: 'test-entity',
|
||||
operationType: 'delete',
|
||||
isFieldPermissionsEnabled: true,
|
||||
objectMetadataMaps: mockInternalContext.objectMetadataMaps,
|
||||
objectsPermissions: mockPermissionOptions.objectRecordsPermissions,
|
||||
selectedColumns: [],
|
||||
|
||||
+1
-6
@@ -410,8 +410,6 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
selectedColumns,
|
||||
allFieldsSelected: false,
|
||||
updatedColumns,
|
||||
isFieldPermissionsEnabled:
|
||||
this.getFeatureFlagMap().IS_FIELDS_PERMISSIONS_ENABLED,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1214,14 +1212,11 @@ export class WorkspaceEntityManager extends EntityManager {
|
||||
entities: createdEntities,
|
||||
});
|
||||
|
||||
const isFieldPermissionsEnabled =
|
||||
this.getFeatureFlagMap().IS_FIELDS_PERMISSIONS_ENABLED;
|
||||
|
||||
const permissionCheckApplies =
|
||||
permissionOptionsFromArgs?.shouldBypassPermissionChecks !== true &&
|
||||
objectMetadataItem.isSystem !== true;
|
||||
|
||||
if (isFieldPermissionsEnabled && permissionCheckApplies) {
|
||||
if (permissionCheckApplies) {
|
||||
formattedResult = this.getFormattedResultWithoutNonReadableFields({
|
||||
formattedResult,
|
||||
objectMetadataItem,
|
||||
|
||||
@@ -41,7 +41,6 @@ type ValidateOperationIsPermittedOrThrowArgs = {
|
||||
objectsPermissions: ObjectsPermissionsDeprecated;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
selectedColumns: string[] | '*';
|
||||
isFieldPermissionsEnabled?: boolean;
|
||||
allFieldsSelected: boolean;
|
||||
updatedColumns: string[];
|
||||
};
|
||||
@@ -52,7 +51,6 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
objectsPermissions,
|
||||
objectMetadataMaps,
|
||||
selectedColumns,
|
||||
isFieldPermissionsEnabled,
|
||||
allFieldsSelected,
|
||||
updatedColumns,
|
||||
}: ValidateOperationIsPermittedOrThrowArgs) => {
|
||||
@@ -81,9 +79,8 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const columnNameToFieldMetadataIdMap = isFieldPermissionsEnabled
|
||||
? getColumnNameToFieldMetadataIdMap(objectMetadata)
|
||||
: {};
|
||||
const columnNameToFieldMetadataIdMap =
|
||||
getColumnNameToFieldMetadataIdMap(objectMetadata);
|
||||
|
||||
const permissionsForEntity = objectsPermissions[objectMetadataIdForEntity];
|
||||
|
||||
@@ -96,14 +93,12 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldPermissionsEnabled) {
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
allFieldsSelected,
|
||||
});
|
||||
}
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
allFieldsSelected,
|
||||
});
|
||||
break;
|
||||
case 'insert':
|
||||
case 'update':
|
||||
@@ -114,15 +109,13 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldPermissionsEnabled) {
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
}
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
|
||||
if (isFieldPermissionsEnabled && updatedColumns.length > 0) {
|
||||
if (updatedColumns.length > 0) {
|
||||
validateUpdateFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
updatedColumns,
|
||||
@@ -138,13 +131,11 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldPermissionsEnabled) {
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
}
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
break;
|
||||
case 'restore':
|
||||
case 'soft-delete':
|
||||
@@ -155,13 +146,11 @@ export const validateOperationIsPermittedOrThrow = ({
|
||||
);
|
||||
}
|
||||
|
||||
if (isFieldPermissionsEnabled) {
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
}
|
||||
validateReadFieldPermissionOrThrow({
|
||||
restrictedFields: permissionsForEntity.restrictedFields,
|
||||
selectedColumns,
|
||||
columnNameToFieldMetadataIdMap,
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new PermissionsException(
|
||||
@@ -180,7 +169,6 @@ type ValidateQueryIsPermittedOrThrowArgs = {
|
||||
objectsPermissions: ObjectsPermissionsDeprecated;
|
||||
objectMetadataMaps: ObjectMetadataMaps;
|
||||
shouldBypassPermissionChecks: boolean;
|
||||
isFieldPermissionsEnabled?: boolean;
|
||||
};
|
||||
|
||||
export const validateQueryIsPermittedOrThrow = ({
|
||||
@@ -188,7 +176,6 @@ export const validateQueryIsPermittedOrThrow = ({
|
||||
objectsPermissions,
|
||||
objectMetadataMaps,
|
||||
shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled,
|
||||
}: ValidateQueryIsPermittedOrThrowArgs) => {
|
||||
if (shouldBypassPermissionChecks) {
|
||||
return;
|
||||
@@ -204,31 +191,29 @@ export const validateQueryIsPermittedOrThrow = ({
|
||||
let selectedColumns: string[] | '*' = [];
|
||||
let updatedColumns: string[] = [];
|
||||
|
||||
if (isFieldPermissionsEnabled) {
|
||||
selectedColumns = getSelectedColumnsFromExpressionMap({
|
||||
operationType,
|
||||
expressionMap,
|
||||
allFieldsSelected,
|
||||
});
|
||||
selectedColumns = getSelectedColumnsFromExpressionMap({
|
||||
operationType,
|
||||
expressionMap,
|
||||
allFieldsSelected,
|
||||
});
|
||||
|
||||
if (operationType !== 'select') {
|
||||
const valuesSet = expressionMap.valuesSet;
|
||||
if (operationType !== 'select') {
|
||||
const valuesSet = expressionMap.valuesSet;
|
||||
|
||||
if (Array.isArray(valuesSet)) {
|
||||
updatedColumns = valuesSet.reduce((acc, value) => {
|
||||
const keys = Object.keys(value);
|
||||
if (Array.isArray(valuesSet)) {
|
||||
updatedColumns = valuesSet.reduce((acc, value) => {
|
||||
const keys = Object.keys(value);
|
||||
|
||||
keys.forEach((key) => {
|
||||
if (!acc.includes(key)) {
|
||||
acc.push(key);
|
||||
}
|
||||
});
|
||||
keys.forEach((key) => {
|
||||
if (!acc.includes(key)) {
|
||||
acc.push(key);
|
||||
}
|
||||
});
|
||||
|
||||
return acc;
|
||||
}, []);
|
||||
} else {
|
||||
updatedColumns = Object.keys(valuesSet ?? {});
|
||||
}
|
||||
return acc;
|
||||
}, []);
|
||||
} else {
|
||||
updatedColumns = Object.keys(valuesSet ?? {});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,7 +223,6 @@ export const validateQueryIsPermittedOrThrow = ({
|
||||
objectsPermissions,
|
||||
objectMetadataMaps,
|
||||
selectedColumns,
|
||||
isFieldPermissionsEnabled,
|
||||
allFieldsSelected,
|
||||
updatedColumns,
|
||||
});
|
||||
|
||||
-3
@@ -14,7 +14,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
@@ -72,8 +71,6 @@ export class WorkspaceDeleteQueryBuilder<
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
-3
@@ -12,7 +12,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type';
|
||||
import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type';
|
||||
@@ -107,8 +106,6 @@ export class WorkspaceInsertQueryBuilder<
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
-5
@@ -10,7 +10,6 @@ import { type FeatureFlagMap } from 'src/engine/core-modules/feature-flag/interf
|
||||
import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/workspace-internal-context.interface';
|
||||
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import {
|
||||
PermissionsException,
|
||||
PermissionsExceptionCode,
|
||||
@@ -321,15 +320,11 @@ export class WorkspaceSelectQueryBuilder<
|
||||
}
|
||||
|
||||
private validatePermissions(): void {
|
||||
const isFieldPermissionsEnabled =
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED];
|
||||
|
||||
validateQueryIsPermittedOrThrow({
|
||||
expressionMap: this.expressionMap,
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
-3
@@ -12,7 +12,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { computeTwentyORMException } from 'src/engine/twenty-orm/error-handling/compute-twenty-orm-exception';
|
||||
import {
|
||||
TwentyORMException,
|
||||
@@ -69,8 +68,6 @@ export class WorkspaceSoftDeleteQueryBuilder<
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
|
||||
-5
@@ -13,7 +13,6 @@ import { type WorkspaceInternalContext } from 'src/engine/twenty-orm/interfaces/
|
||||
|
||||
import { DatabaseEventAction } from 'src/engine/api/graphql/graphql-query-runner/enums/database-event-action';
|
||||
import { type AuthContext } from 'src/engine/core-modules/auth/types/auth-context.type';
|
||||
import { FeatureFlagKey } from 'src/engine/core-modules/feature-flag/enums/feature-flag-key.enum';
|
||||
import { type QueryDeepPartialEntityWithNestedRelationFields } from 'src/engine/twenty-orm/entity-manager/types/query-deep-partial-entity-with-nested-relation-fields.type';
|
||||
import { type RelationConnectQueryConfig } from 'src/engine/twenty-orm/entity-manager/types/relation-connect-query-config.type';
|
||||
import { type RelationDisconnectQueryFieldsByEntityIndex } from 'src/engine/twenty-orm/entity-manager/types/relation-nested-query-fields-by-entity-index.type';
|
||||
@@ -91,8 +90,6 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
|
||||
const mainAliasTarget = this.getMainAliasTarget();
|
||||
@@ -202,8 +199,6 @@ export class WorkspaceUpdateQueryBuilder<
|
||||
objectsPermissions: this.objectRecordsPermissions,
|
||||
objectMetadataMaps: this.internalContext.objectMetadataMaps,
|
||||
shouldBypassPermissionChecks: this.shouldBypassPermissionChecks,
|
||||
isFieldPermissionsEnabled:
|
||||
this.featureFlagMap?.[FeatureFlagKey.IS_FIELDS_PERMISSIONS_ENABLED],
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user