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,
|
||||
|
||||
Reference in New Issue
Block a user