Unify auth context → role permission config resolution into a single pure utility (#18927)
## Summary - Consolidates duplicated auth-context-to-role-ID resolution logic (previously in `PermissionsService.resolveRolePermissionConfigFromAuthContext` and `CommonBaseQueryRunnerService.getRoleIdOrThrow`) into a single pure utility function `resolveRolePermissionConfig` in the ORM layer - The utility is synchronous and operates on cached data (`userWorkspaceRoleMap`, `apiKeyRoleMap`) already loaded into the workspace context — no async calls, no service dependencies - Adds `apiKeyRoleMap` to `ORMWorkspaceContext` (it was already in the workspace cache, just not loaded into the ORM context) - Removes `PermissionsService` dependency from `NavigationMenuItemRecordIdentifierService` - Removes `UserRoleService` and `ApiKeyRoleService` injections from `CommonBaseQueryRunnerService` ## Test plan - [ ] Existing typecheck passes (`npx nx typecheck twenty-server`) - [ ] Verify record identifier resolution still works for navigation menu items (user, system, API key, and application auth contexts) - [ ] Verify GraphQL CRUD queries still enforce correct role-based permissions - [ ] Verify API key authenticated requests resolve permissions correctly Made with [Cursor](https://cursor.com)
This commit is contained in:
+17
-52
@@ -1,7 +1,6 @@
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
|
||||
import { type PermissionFlagType } from 'twenty-shared/constants';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { QueryResultFieldValue } from 'src/engine/api/graphql/workspace-query-runner/factories/query-result-getters/interfaces/query-result-field-value';
|
||||
|
||||
@@ -29,9 +28,7 @@ import { OBJECTS_WITH_SETTINGS_PERMISSIONS_REQUIREMENTS } from 'src/engine/api/g
|
||||
import { GraphqlQueryParser } from 'src/engine/api/graphql/graphql-query-runner/graphql-query-parsers/graphql-query.parser';
|
||||
import { WorkspacePreQueryHookPayload } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/types/workspace-query-hook.type';
|
||||
import { WorkspaceQueryHookService } from 'src/engine/api/graphql/workspace-query-runner/workspace-query-hook/workspace-query-hook.service';
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service';
|
||||
import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard';
|
||||
import { isApplicationAuthContext } from 'src/engine/core-modules/auth/guards/is-application-auth-context.guard';
|
||||
import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard';
|
||||
import { WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
|
||||
import { FeatureFlagService } from 'src/engine/core-modules/feature-flag/services/feature-flag.service';
|
||||
@@ -48,9 +45,9 @@ import {
|
||||
PermissionsExceptionMessage,
|
||||
} from 'src/engine/metadata-modules/permissions/permissions.exception';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import type { RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { getWorkspaceContext } from 'src/engine/twenty-orm/storage/orm-workspace-context.storage';
|
||||
import { resolveRolePermissionConfig } from 'src/engine/twenty-orm/utils/resolve-role-permission-config.util';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
|
||||
@Injectable()
|
||||
@@ -73,10 +70,6 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
@Inject()
|
||||
protected readonly permissionsService: PermissionsService;
|
||||
@Inject()
|
||||
protected readonly userRoleService: UserRoleService;
|
||||
@Inject()
|
||||
protected readonly apiKeyRoleService: ApiKeyRoleService;
|
||||
@Inject()
|
||||
protected readonly workspaceCacheService: WorkspaceCacheService;
|
||||
@Inject()
|
||||
protected readonly commonResultGettersService: CommonResultGettersService;
|
||||
@@ -140,7 +133,6 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
async () =>
|
||||
this.executeQueryAndEnrichResults(
|
||||
processedArgs,
|
||||
authContext,
|
||||
queryRunnerContext,
|
||||
commonQueryParser,
|
||||
),
|
||||
@@ -205,13 +197,11 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
private async executeQueryAndEnrichResults(
|
||||
processedArgs: CommonExtendedInput<Args>,
|
||||
authContext: WorkspaceAuthContext,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
commonQueryParser: GraphqlQueryParser,
|
||||
): Promise<Output> {
|
||||
const extendedQueryRunnerContext =
|
||||
await this.prepareExtendedQueryRunnerContextWithGlobalDatasource(
|
||||
authContext,
|
||||
queryRunnerContext,
|
||||
);
|
||||
|
||||
@@ -223,7 +213,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
return this.enrichResultsWithGettersAndHooks({
|
||||
results,
|
||||
operationName: this.operationName,
|
||||
authContext,
|
||||
authContext: extendedQueryRunnerContext.authContext,
|
||||
flatObjectMetadata: queryRunnerContext.flatObjectMetadata,
|
||||
flatObjectMetadataMaps: queryRunnerContext.flatObjectMetadataMaps,
|
||||
flatFieldMetadataMaps: queryRunnerContext.flatFieldMetadataMaps,
|
||||
@@ -302,49 +292,24 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
}
|
||||
}
|
||||
|
||||
private async getRoleIdOrThrow(
|
||||
authContext: WorkspaceAuthContext,
|
||||
workspaceId: string,
|
||||
): Promise<string> {
|
||||
if (isApiKeyAuthContext(authContext)) {
|
||||
return this.apiKeyRoleService.getRoleIdForApiKeyId(
|
||||
authContext.apiKey.id,
|
||||
workspaceId,
|
||||
);
|
||||
}
|
||||
|
||||
if (
|
||||
isApplicationAuthContext(authContext) &&
|
||||
isDefined(authContext.application.defaultRoleId)
|
||||
) {
|
||||
return authContext.application.defaultRoleId;
|
||||
}
|
||||
|
||||
if (isUserAuthContext(authContext)) {
|
||||
return this.userRoleService.getRoleIdForUserWorkspace({
|
||||
userWorkspaceId: authContext.userWorkspaceId,
|
||||
workspaceId,
|
||||
});
|
||||
}
|
||||
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
private async prepareExtendedQueryRunnerContextWithGlobalDatasource(
|
||||
authContext: WorkspaceAuthContext,
|
||||
queryRunnerContext: CommonBaseQueryRunnerContext,
|
||||
): Promise<Omit<CommonExtendedQueryRunnerContext, 'commonQueryParser'>> {
|
||||
const workspaceId = authContext.workspace.id;
|
||||
const context = getWorkspaceContext();
|
||||
|
||||
const roleId = await this.getRoleIdOrThrow(authContext, workspaceId);
|
||||
const rolePermissionConfig = resolveRolePermissionConfig({
|
||||
authContext: context.authContext,
|
||||
userWorkspaceRoleMap: context.userWorkspaceRoleMap,
|
||||
apiKeyRoleMap: context.apiKeyRoleMap,
|
||||
});
|
||||
|
||||
const rolePermissionConfig: RolePermissionConfig = {
|
||||
intersectionOf: [roleId],
|
||||
};
|
||||
if (!rolePermissionConfig) {
|
||||
throw new CommonQueryRunnerException(
|
||||
'Invalid auth context',
|
||||
CommonQueryRunnerExceptionCode.INVALID_AUTH_CONTEXT,
|
||||
{ userFriendlyMessage: STANDARD_ERROR_MESSAGE },
|
||||
);
|
||||
}
|
||||
|
||||
const globalWorkspaceDataSource = this.isReadOnly
|
||||
? await this.globalWorkspaceOrmManager.getGlobalWorkspaceDataSourceReplica()
|
||||
@@ -357,7 +322,7 @@ export abstract class CommonBaseQueryRunnerService<
|
||||
|
||||
return {
|
||||
...queryRunnerContext,
|
||||
authContext,
|
||||
authContext: context.authContext,
|
||||
workspaceDataSource: globalWorkspaceDataSource,
|
||||
rolePermissionConfig,
|
||||
repository,
|
||||
|
||||
@@ -4,16 +4,9 @@ import { FileModule } from 'src/engine/core-modules/file/file.module';
|
||||
import { SearchResolver } from 'src/engine/core-modules/search/search.resolver';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheModule } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.module';
|
||||
import { PermissionsModule } from 'src/engine/metadata-modules/permissions/permissions.module';
|
||||
import { UserRoleModule } from 'src/engine/metadata-modules/user-role/user-role.module';
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
FileModule,
|
||||
WorkspaceManyOrAllFlatEntityMapsCacheModule,
|
||||
UserRoleModule,
|
||||
PermissionsModule,
|
||||
],
|
||||
imports: [FileModule, WorkspaceManyOrAllFlatEntityMapsCacheModule],
|
||||
providers: [SearchResolver, SearchService],
|
||||
})
|
||||
export class SearchModule {}
|
||||
|
||||
@@ -4,8 +4,6 @@ import { Args, Query } from '@nestjs/graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { CoreResolver } from 'src/engine/api/graphql/graphql-config/decorators/core-resolver.decorator';
|
||||
import { ApiKeyEntity } from 'src/engine/core-modules/api-key/api-key.entity';
|
||||
import { ApiKeyRoleService } from 'src/engine/core-modules/api-key/services/api-key-role.service';
|
||||
import { PreventNestToAutoLogGraphqlErrorsFilter } from 'src/engine/core-modules/graphql/filters/prevent-nest-to-auto-log-graphql-errors.filter';
|
||||
import { ResolverValidationPipe } from 'src/engine/core-modules/graphql/pipes/resolver-validation.pipe';
|
||||
import { SearchArgs } from 'src/engine/core-modules/search/dtos/search-args';
|
||||
@@ -13,14 +11,10 @@ import { SearchResultConnectionDTO } from 'src/engine/core-modules/search/dtos/s
|
||||
import { SearchApiExceptionFilter } from 'src/engine/core-modules/search/filters/search-api-exception.filter';
|
||||
import { SearchService } from 'src/engine/core-modules/search/services/search.service';
|
||||
import { WorkspaceEntity } from 'src/engine/core-modules/workspace/workspace.entity';
|
||||
import { AuthApiKey } from 'src/engine/decorators/auth/auth-api-key.decorator';
|
||||
import { AuthUserWorkspaceId } from 'src/engine/decorators/auth/auth-user-workspace-id.decorator';
|
||||
import { AuthWorkspace } from 'src/engine/decorators/auth/auth-workspace.decorator';
|
||||
import { CustomPermissionGuard } from 'src/engine/guards/custom-permission.guard';
|
||||
import { WorkspaceAuthGuard } from 'src/engine/guards/workspace-auth.guard';
|
||||
import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadata-modules/flat-entity/services/workspace-many-or-all-flat-entity-maps-cache.service';
|
||||
import { UserRoleService } from 'src/engine/metadata-modules/user-role/user-role.service';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
@CoreResolver()
|
||||
@UseFilters(SearchApiExceptionFilter, PreventNestToAutoLogGraphqlErrorsFilter)
|
||||
@@ -30,16 +24,11 @@ export class SearchResolver {
|
||||
constructor(
|
||||
private readonly searchService: SearchService,
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly userRoleService: UserRoleService,
|
||||
private readonly apiKeyRoleService: ApiKeyRoleService,
|
||||
) {}
|
||||
|
||||
@Query(() => SearchResultConnectionDTO)
|
||||
async search(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
@AuthUserWorkspaceId({ allowUndefined: true })
|
||||
userWorkspaceId: string | undefined,
|
||||
@AuthApiKey() apiKey: ApiKeyEntity | undefined,
|
||||
@Args()
|
||||
{
|
||||
searchInput,
|
||||
@@ -69,29 +58,6 @@ export class SearchResolver {
|
||||
excludedObjectNameSingulars: excludedObjectNameSingulars ?? [],
|
||||
});
|
||||
|
||||
// TODO: move to a service
|
||||
let rolePermissionConfig: RolePermissionConfig | undefined;
|
||||
|
||||
if (isDefined(apiKey)) {
|
||||
const roleId = await this.apiKeyRoleService.getRoleIdForApiKeyId(
|
||||
apiKey.id,
|
||||
workspace.id,
|
||||
);
|
||||
|
||||
if (isDefined(roleId)) {
|
||||
rolePermissionConfig = { unionOf: [roleId] };
|
||||
}
|
||||
} else if (isDefined(userWorkspaceId)) {
|
||||
const roleId = await this.userRoleService.getRoleIdForUserWorkspace({
|
||||
userWorkspaceId,
|
||||
workspaceId: workspace.id,
|
||||
});
|
||||
|
||||
if (isDefined(roleId)) {
|
||||
rolePermissionConfig = { unionOf: [roleId] };
|
||||
}
|
||||
}
|
||||
|
||||
const allRecordsWithObjectMetadataItems =
|
||||
await this.searchService.getAllRecordsWithObjectMetadataItems({
|
||||
flatObjectMetadatas: filteredObjectMetadataItems,
|
||||
@@ -103,7 +69,6 @@ export class SearchResolver {
|
||||
excludedObjectNameSingulars,
|
||||
after,
|
||||
workspaceId: workspace.id,
|
||||
rolePermissionConfig,
|
||||
});
|
||||
|
||||
return this.searchService.computeSearchObjectResults({
|
||||
|
||||
@@ -35,7 +35,8 @@ import { type FlatObjectMetadata } from 'src/engine/metadata-modules/flat-object
|
||||
import { SEARCH_VECTOR_FIELD } from 'src/engine/metadata-modules/search-field-metadata/constants/search-vector-field.constants';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { type WorkspaceRepository } from 'src/engine/twenty-orm/repository/workspace.repository';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
import { getWorkspaceContext } from 'src/engine/twenty-orm/storage/orm-workspace-context.storage';
|
||||
import { resolveRolePermissionConfig } from 'src/engine/twenty-orm/utils/resolve-role-permission-config.util';
|
||||
|
||||
type LastRanks = { tsRankCD: number; tsRank: number };
|
||||
|
||||
@@ -64,12 +65,10 @@ export class SearchService {
|
||||
filter,
|
||||
after,
|
||||
workspaceId,
|
||||
rolePermissionConfig,
|
||||
}: {
|
||||
flatObjectMetadatas: FlatObjectMetadata[];
|
||||
flatFieldMetadataMaps: FlatEntityMaps<FlatFieldMetadata>;
|
||||
workspaceId: string;
|
||||
rolePermissionConfig?: RolePermissionConfig;
|
||||
} & SearchArgs) {
|
||||
const filteredObjectMetadataItems = this.filterObjectMetadataItems({
|
||||
flatObjectMetadatas,
|
||||
@@ -90,6 +89,14 @@ export class SearchService {
|
||||
objectMetadataItemChunk.map(async (flatObjectMetadata) => {
|
||||
return this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
async () => {
|
||||
const context = getWorkspaceContext();
|
||||
const rolePermissionConfig =
|
||||
resolveRolePermissionConfig({
|
||||
authContext: context.authContext,
|
||||
userWorkspaceRoleMap: context.userWorkspaceRoleMap,
|
||||
apiKeyRoleMap: context.apiKeyRoleMap,
|
||||
}) ?? undefined;
|
||||
|
||||
const repository =
|
||||
await this.globalWorkspaceOrmManager.getRepository<ObjectRecord>(
|
||||
workspaceId,
|
||||
|
||||
+14
-8
@@ -10,9 +10,10 @@ import { WorkspaceManyOrAllFlatEntityMapsCacheService } from 'src/engine/metadat
|
||||
import { findFlatEntityByIdInFlatEntityMaps } from 'src/engine/metadata-modules/flat-entity/utils/find-flat-entity-by-id-in-flat-entity-maps.util';
|
||||
import { RecordIdentifierDTO } from 'src/engine/metadata-modules/navigation-menu-item/dtos/record-identifier.dto';
|
||||
import { getMinimalSelectForRecordIdentifier } from 'src/engine/metadata-modules/navigation-menu-item/utils/get-minimal-select-for-record-identifier.util';
|
||||
import { PermissionsService } from 'src/engine/metadata-modules/permissions/permissions.service';
|
||||
import { GlobalWorkspaceOrmManager } from 'src/engine/twenty-orm/global-workspace-datasource/global-workspace-orm.manager';
|
||||
import { getWorkspaceContext } from 'src/engine/twenty-orm/storage/orm-workspace-context.storage';
|
||||
import { formatResult } from 'src/engine/twenty-orm/utils/format-result.util';
|
||||
import { resolveRolePermissionConfig } from 'src/engine/twenty-orm/utils/resolve-role-permission-config.util';
|
||||
|
||||
@Injectable()
|
||||
export class NavigationMenuItemRecordIdentifierService {
|
||||
@@ -20,7 +21,6 @@ export class NavigationMenuItemRecordIdentifierService {
|
||||
private readonly workspaceManyOrAllFlatEntityMapsCacheService: WorkspaceManyOrAllFlatEntityMapsCacheService,
|
||||
private readonly globalWorkspaceOrmManager: GlobalWorkspaceOrmManager,
|
||||
private readonly fileService: FileService,
|
||||
private readonly permissionsService: PermissionsService,
|
||||
) {}
|
||||
|
||||
async resolveRecordIdentifier({
|
||||
@@ -63,18 +63,24 @@ export class NavigationMenuItemRecordIdentifierService {
|
||||
workspace: { id: workspaceId },
|
||||
} as WorkspaceAuthContext);
|
||||
|
||||
const rolePermissionConfig =
|
||||
await this.permissionsService.resolveRolePermissionConfigFromAuthContext(
|
||||
resolvedAuthContext,
|
||||
);
|
||||
|
||||
const record =
|
||||
await this.globalWorkspaceOrmManager.executeInWorkspaceContext(
|
||||
async () => {
|
||||
const context = getWorkspaceContext();
|
||||
const rolePermissionConfig = resolveRolePermissionConfig({
|
||||
authContext: context.authContext,
|
||||
userWorkspaceRoleMap: context.userWorkspaceRoleMap,
|
||||
apiKeyRoleMap: context.apiKeyRoleMap,
|
||||
});
|
||||
|
||||
if (!rolePermissionConfig) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const repository = await this.globalWorkspaceOrmManager.getRepository(
|
||||
workspaceId,
|
||||
objectMetadata.nameSingular,
|
||||
rolePermissionConfig ?? { shouldBypassPermissionChecks: true },
|
||||
rolePermissionConfig,
|
||||
);
|
||||
|
||||
const alias = objectMetadata.nameSingular;
|
||||
|
||||
@@ -13,10 +13,6 @@ import {
|
||||
ApplicationExceptionCode,
|
||||
} from 'src/engine/core-modules/application/application.exception';
|
||||
import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard';
|
||||
import { isApplicationAuthContext } from 'src/engine/core-modules/auth/guards/is-application-auth-context.guard';
|
||||
import { isSystemAuthContext } from 'src/engine/core-modules/auth/guards/is-system-auth-context.guard';
|
||||
import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard';
|
||||
import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
|
||||
import { TOOL_PERMISSION_FLAGS } from 'src/engine/metadata-modules/permissions/constants/tool-permission-flags';
|
||||
import {
|
||||
PermissionsException,
|
||||
@@ -134,48 +130,6 @@ export class PermissionsService {
|
||||
objectsPermissions: {},
|
||||
}) as const satisfies UserWorkspacePermissions;
|
||||
|
||||
// TODO: this could likely be handled in the ORM layer
|
||||
public async resolveRolePermissionConfigFromAuthContext(
|
||||
authContext: WorkspaceAuthContext,
|
||||
): Promise<RolePermissionConfig | null> {
|
||||
const workspaceId = authContext.workspace.id;
|
||||
|
||||
if (isSystemAuthContext(authContext)) {
|
||||
return { shouldBypassPermissionChecks: true };
|
||||
}
|
||||
|
||||
if (isApiKeyAuthContext(authContext)) {
|
||||
const roleId = await this.apiKeyRoleService.getRoleIdForApiKeyId(
|
||||
authContext.apiKey.id,
|
||||
workspaceId,
|
||||
);
|
||||
|
||||
return { intersectionOf: [roleId] };
|
||||
}
|
||||
|
||||
if (
|
||||
isApplicationAuthContext(authContext) &&
|
||||
isDefined(authContext.application.defaultRoleId)
|
||||
) {
|
||||
return { intersectionOf: [authContext.application.defaultRoleId] };
|
||||
}
|
||||
|
||||
if (isUserAuthContext(authContext)) {
|
||||
const roleId = await this.userRoleService.getRoleIdForUserWorkspace({
|
||||
userWorkspaceId: authContext.userWorkspaceId,
|
||||
workspaceId,
|
||||
});
|
||||
|
||||
if (!isDefined(roleId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { intersectionOf: [roleId] };
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public async userHasWorkspaceSettingPermission({
|
||||
userWorkspaceId,
|
||||
workspaceId,
|
||||
|
||||
+1
@@ -321,6 +321,7 @@ describe('WorkspaceEntityManager', () => {
|
||||
userWorkspaceRoleMap: {
|
||||
'user-workspace-id': 'role-id',
|
||||
},
|
||||
apiKeyRoleMap: {},
|
||||
};
|
||||
|
||||
setWorkspaceContext(mockWorkspaceContext);
|
||||
|
||||
+4
@@ -93,6 +93,7 @@ export class GlobalWorkspaceOrmManager {
|
||||
rolesPermissions: permissionsPerRoleId,
|
||||
ORMEntityMetadatas: entityMetadatas,
|
||||
userWorkspaceRoleMap,
|
||||
apiKeyRoleMap,
|
||||
flatRowLevelPermissionPredicateMaps,
|
||||
flatRowLevelPermissionPredicateGroupMaps,
|
||||
} = await this.workspaceCacheService.getOrRecompute(workspaceId, [
|
||||
@@ -103,6 +104,7 @@ export class GlobalWorkspaceOrmManager {
|
||||
'rolesPermissions',
|
||||
'ORMEntityMetadatas',
|
||||
'userWorkspaceRoleMap',
|
||||
'apiKeyRoleMap',
|
||||
'flatRowLevelPermissionPredicateMaps',
|
||||
'flatRowLevelPermissionPredicateGroupMaps',
|
||||
]);
|
||||
@@ -122,6 +124,7 @@ export class GlobalWorkspaceOrmManager {
|
||||
permissionsPerRoleId,
|
||||
entityMetadatas,
|
||||
userWorkspaceRoleMap,
|
||||
apiKeyRoleMap,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -167,6 +170,7 @@ export class GlobalWorkspaceOrmManager {
|
||||
permissionsPerRoleId: {},
|
||||
entityMetadatas,
|
||||
userWorkspaceRoleMap: {},
|
||||
apiKeyRoleMap: {},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ export type ORMWorkspaceContext = {
|
||||
permissionsPerRoleId: ObjectsPermissionsByRoleId;
|
||||
entityMetadatas: EntityMetadata[];
|
||||
userWorkspaceRoleMap: UserWorkspaceRoleMap;
|
||||
apiKeyRoleMap: Record<string, string>;
|
||||
flatRowLevelPermissionPredicateMaps: FlatRowLevelPermissionPredicateMaps;
|
||||
flatRowLevelPermissionPredicateGroupMaps: FlatRowLevelPermissionPredicateGroupMaps;
|
||||
};
|
||||
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
import { isApiKeyAuthContext } from 'src/engine/core-modules/auth/guards/is-api-key-auth-context.guard';
|
||||
import { isApplicationAuthContext } from 'src/engine/core-modules/auth/guards/is-application-auth-context.guard';
|
||||
import { isSystemAuthContext } from 'src/engine/core-modules/auth/guards/is-system-auth-context.guard';
|
||||
import { isUserAuthContext } from 'src/engine/core-modules/auth/guards/is-user-auth-context.guard';
|
||||
import { type WorkspaceAuthContext } from 'src/engine/core-modules/auth/types/workspace-auth-context.type';
|
||||
import { type UserWorkspaceRoleMap } from 'src/engine/metadata-modules/role-target/types/user-workspace-role-map';
|
||||
import { type RolePermissionConfig } from 'src/engine/twenty-orm/types/role-permission-config';
|
||||
|
||||
export const resolveRolePermissionConfig = ({
|
||||
authContext,
|
||||
userWorkspaceRoleMap,
|
||||
apiKeyRoleMap,
|
||||
}: {
|
||||
authContext: WorkspaceAuthContext;
|
||||
userWorkspaceRoleMap: UserWorkspaceRoleMap;
|
||||
apiKeyRoleMap: Record<string, string>;
|
||||
}): RolePermissionConfig | null => {
|
||||
if (isSystemAuthContext(authContext)) {
|
||||
return { shouldBypassPermissionChecks: true };
|
||||
}
|
||||
|
||||
if (isApiKeyAuthContext(authContext)) {
|
||||
const roleId = apiKeyRoleMap[authContext.apiKey.id];
|
||||
|
||||
if (!isDefined(roleId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { intersectionOf: [roleId] };
|
||||
}
|
||||
|
||||
if (
|
||||
isApplicationAuthContext(authContext) &&
|
||||
isDefined(authContext.application.defaultRoleId)
|
||||
) {
|
||||
return { intersectionOf: [authContext.application.defaultRoleId] };
|
||||
}
|
||||
|
||||
if (isUserAuthContext(authContext)) {
|
||||
const roleId = userWorkspaceRoleMap[authContext.userWorkspaceId];
|
||||
|
||||
if (!isDefined(roleId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return { intersectionOf: [roleId] };
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
Reference in New Issue
Block a user