feat(core): Add credentialTypePolicy:manage as a separate permission (no-changelog) (#38855)

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Rúben Castro 2026-09-18 11:43:09 +00:00 committed by GitHub
parent 6f2bc13984
commit 1b6b20c29e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 71 additions and 0 deletions

View File

@ -47,6 +47,7 @@ exports[`Scope Information > ensure scopes are defined correctly 1`] = `
"credential:update",
"credential:delete",
"credential:list",
"credentialTypePolicy:manage",
"externalSecretsProvider:sync",
"externalSecretsProvider:create",
"externalSecretsProvider:read",

View File

@ -22,6 +22,7 @@ export const RESOURCES = {
'manageInstance',
...DEFAULT_OPERATIONS,
] as const,
credentialTypePolicy: ['manage'] as const,
externalSecretsProvider: ['sync', ...DEFAULT_OPERATIONS] as const,
externalSecret: ['list'] as const,
eventBusDestination: ['test', ...DEFAULT_OPERATIONS] as const,
@ -125,6 +126,7 @@ export const API_KEY_RESOURCES = {
role: ['manage', 'manageProject', 'list', 'read'] as const,
roleMappingRule: ['create', 'delete', 'list', 'update'] as const,
nodeTypePolicy: ['manage'] as const,
// TODO: add credentialTypePolicy:manage once a Public API endpoint consumes it.
} as const;
export const GLOBAL_OWNER_ROLE_SLUG = 'global:owner';

View File

@ -0,0 +1,58 @@
import { API_KEY_RESOURCES } from '@/constants.ee';
import { GLOBAL_ADMIN_SCOPES, GLOBAL_OWNER_SCOPES } from '@/roles/scopes/global-scopes.ee';
import {
PERSONAL_PROJECT_OWNER_SCOPES,
PROJECT_EDITOR_SCOPES,
PROJECT_VIEWER_SCOPES,
REGULAR_PROJECT_ADMIN_SCOPES,
} from '@/roles/scopes/project-scopes.ee';
import type { AuthPrincipal } from '@/types.ee';
import { hasGlobalScope } from '@/utilities/has-global-scope.ee';
const principal = (slug: string, scopes: string[]): AuthPrincipal =>
({
role: { slug, scopes: scopes.map((scope) => ({ slug: scope })) },
}) as unknown as AuthPrincipal;
describe('credentialTypePolicy:manage and nodeTypePolicy:manage are independent scopes', () => {
it('a user granted only nodeTypePolicy:manage does not also gain credentialTypePolicy:manage', () => {
expect(
hasGlobalScope(
principal('custom:node-policy-manager-only', ['nodeTypePolicy:manage']),
'credentialTypePolicy:manage',
),
).toBe(false);
});
it('a user granted only credentialTypePolicy:manage does not also gain nodeTypePolicy:manage', () => {
expect(
hasGlobalScope(
principal('custom:credential-policy-manager-only', ['credentialTypePolicy:manage']),
'nodeTypePolicy:manage',
),
).toBe(false);
});
});
describe('credentialTypePolicy:manage default grants', () => {
it('is granted to instance owners and admins', () => {
expect(GLOBAL_OWNER_SCOPES).toContain('credentialTypePolicy:manage');
expect(GLOBAL_ADMIN_SCOPES).toContain('credentialTypePolicy:manage');
});
it('is granted to project admins, same as nodeTypePolicy:manage', () => {
expect(REGULAR_PROJECT_ADMIN_SCOPES).toContain('credentialTypePolicy:manage');
});
it('is not granted to personal project owners, editors, or viewers', () => {
expect(PERSONAL_PROJECT_OWNER_SCOPES).not.toContain('credentialTypePolicy:manage');
expect(PROJECT_EDITOR_SCOPES).not.toContain('credentialTypePolicy:manage');
expect(PROJECT_VIEWER_SCOPES).not.toContain('credentialTypePolicy:manage');
});
});
describe('credentialTypePolicy:manage as an API key scope', () => {
it('is not declared in API_KEY_RESOURCES yet, since no Public API endpoint consumes it', () => {
expect(API_KEY_RESOURCES).not.toHaveProperty('credentialTypePolicy');
});
});

View File

@ -30,6 +30,7 @@ export const GLOBAL_OWNER_SCOPES: Scope[] = [
'credential:connect',
'credential:createEndUser',
'credential:manageInstance',
'credentialTypePolicy:manage',
'community:register',
'communityPackage:install',
'communityPackage:uninstall',

View File

@ -54,6 +54,7 @@ export const REGULAR_PROJECT_ADMIN_SCOPES: Scope[] = [
'folder:move',
'sourceControl:push',
'nodeTypePolicy:manage',
'credentialTypePolicy:manage',
'dataTable:create',
'dataTable:delete',
'dataTable:read',

View File

@ -67,6 +67,11 @@ export const scopeInformation: Partial<Record<Scope, ScopeInformation>> = {
displayName: 'Manage Node Type Policies',
description: 'Allows managing which node types are available, on the instance or in a project.',
},
'credentialTypePolicy:manage': {
displayName: 'Manage Credential Type Policies',
description:
'Allows managing which credential types are available, on the instance or in a project.',
},
'encryptionKey:manage': {
displayName: 'Manage Encryption Keys',
description: 'Allows listing and rotating instance encryption keys.',

View File

@ -16,6 +16,7 @@ describe('permissions', () => {
community: {},
communityPackage: {},
credential: {},
credentialTypePolicy: {},
externalSecretsProvider: {},
externalSecret: {},
eventBusDestination: {},
@ -113,6 +114,7 @@ describe('permissions', () => {
share: true,
update: true,
},
credentialTypePolicy: {},
eventBusDestination: {
list: true,
test: true,

View File

@ -22,6 +22,7 @@ export const useRBACStore = defineStore(STORES.RBAC, () => {
annotationTag: {},
user: {},
credential: {},
credentialTypePolicy: {},
variable: {},
projectVariable: {},
sourceControl: {},