Fix: allow API key creation without Roles permission (#23102)
## Problem A user with the **API keys & webhooks** permission but **without** the **Roles** setting permission cannot create an API key through the UI. The role selector relies on the `getRoles` query, which is guarded by the `ROLES` permission, so the roles list comes back empty, `SettingsDevelopersRoleSelector` early-returns, and no role can be selected — leaving the form unsavable. <img width="1058" height="408" alt="Screenshot 2026-07-21 at 13 38 34" src="https://github.com/user-attachments/assets/fe97ba78-e116-458d-af10-11c5969c4636" /> ## Fix Expose the assignable roles through the API-key permission scope so users can **pick** a role to assign to an API key without being able to **edit** roles. - **Backend**: add `getApiKeyRoles` query on `ApiKeyResolver` (already guarded by `API_KEYS_AND_WEBHOOKS`), backed by `ApiKeyRoleService.getApiKeyAssignableRoles` which returns roles where `canBeAssignedToApiKeys = true`. - **Frontend**: add a `GetApiKeyRoles` query and use it in the API key create and detail pages instead of `getRoles`. The role selector prop type is narrowed to the fields it actually uses. <img width="1025" height="455" alt="Screenshot 2026-07-21 at 13 45 01" src="https://github.com/user-attachments/assets/f1be8f97-5a30-4afc-9eee-c928f4607471" />
This commit is contained in:
@@ -43,6 +43,13 @@ export class ApiKeyResolver {
|
||||
return this.apiKeyService.findActiveByWorkspaceId(workspace.id);
|
||||
}
|
||||
|
||||
@Query(() => [RoleDTO])
|
||||
async getApiKeyRoles(
|
||||
@AuthWorkspace() workspace: WorkspaceEntity,
|
||||
): Promise<RoleDTO[]> {
|
||||
return this.apiKeyRoleService.getApiKeyAssignableRoles(workspace.id);
|
||||
}
|
||||
|
||||
@Query(() => ApiKeyEntity, { nullable: true })
|
||||
async apiKey(
|
||||
@Args('input') input: GetApiKeyInput,
|
||||
|
||||
+15
-1
@@ -14,7 +14,10 @@ import { RoleTargetService } from 'src/engine/metadata-modules/role-target/servi
|
||||
import { type RoleDTO } from 'src/engine/metadata-modules/role/dtos/role.dto';
|
||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||
import { fromFlatRoleToRoleDto } from 'src/engine/metadata-modules/role/utils/fromFlatRoleToRoleDto.util';
|
||||
import { fromRoleEntityToRoleDto } from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||
import {
|
||||
fromRoleEntitiesToRoleDtos,
|
||||
fromRoleEntityToRoleDto,
|
||||
} from 'src/engine/metadata-modules/role/utils/fromRoleEntityToRoleDto.util';
|
||||
import { InjectWorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/inject-workspace-scoped-repository.decorator';
|
||||
import { WorkspaceScopedRepository } from 'src/engine/twenty-orm/workspace-scoped-repository/workspace-scoped-repository';
|
||||
import { WorkspaceCacheService } from 'src/engine/workspace-cache/services/workspace-cache.service';
|
||||
@@ -173,6 +176,17 @@ export class ApiKeyRoleService {
|
||||
};
|
||||
}
|
||||
|
||||
public async getApiKeyAssignableRoles(
|
||||
workspaceId: string,
|
||||
): Promise<RoleDTO[]> {
|
||||
const roles = await this.roleRepository.find(workspaceId, {
|
||||
where: { canBeAssignedToApiKeys: true },
|
||||
order: { label: 'ASC' },
|
||||
});
|
||||
|
||||
return fromRoleEntitiesToRoleDtos(roles);
|
||||
}
|
||||
|
||||
public async getRolesByApiKeys({
|
||||
apiKeyIds,
|
||||
workspaceId,
|
||||
|
||||
Reference in New Issue
Block a user