mirror of
https://github.com/n8n-io/n8n.git
synced 2026-10-17 13:39:44 +02:00
fix(API): Let global members grant variable scopes to API keys (#40064)
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
parent
01e5ebf726
commit
c95e34f97a
|
|
@ -146,6 +146,10 @@ export const MEMBER_API_KEY_SCOPES: ApiKeyScope[] = [
|
|||
'credential:update',
|
||||
'credential:move',
|
||||
'credential:delete',
|
||||
'variable:create',
|
||||
'variable:delete',
|
||||
'variable:list',
|
||||
'variable:update',
|
||||
'dataTable:create',
|
||||
'dataTable:read',
|
||||
'dataTable:update',
|
||||
|
|
@ -206,6 +210,11 @@ export const API_KEY_SCOPES_FOR_IMPLICIT_PERSONAL_PROJECT: ApiKeyScope[] = [
|
|||
'credential:update',
|
||||
'credential:move',
|
||||
'credential:delete',
|
||||
// Members hold these for team projects where they are editor or admin.
|
||||
// The variables service checks the project role on every request.
|
||||
'variable:create',
|
||||
'variable:delete',
|
||||
'variable:update',
|
||||
'dataTable:create',
|
||||
'dataTable:read',
|
||||
'dataTable:update',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,11 @@
|
|||
import { testDb } from '@n8n/backend-test-utils';
|
||||
import { ApiKeyRepository, GLOBAL_MEMBER_ROLE, GLOBAL_OWNER_ROLE } from '@n8n/db';
|
||||
import { Container } from '@n8n/di';
|
||||
import { getOwnerOnlyApiKeyScopes, type ApiKeyScope } from '@n8n/permissions';
|
||||
import {
|
||||
getOwnerOnlyApiKeyScopes,
|
||||
MEMBER_API_KEY_SCOPES,
|
||||
type ApiKeyScope,
|
||||
} from '@n8n/permissions';
|
||||
import type { InstanceSettings } from 'n8n-core';
|
||||
import { mock } from 'vitest-mock-extended';
|
||||
|
||||
|
|
@ -70,6 +74,18 @@ describe('PublicApiKeyService', () => {
|
|||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it('should keep every scope a member may hold', async () => {
|
||||
const adminUser = await createAdminWithApiKey({
|
||||
scopes: [...MEMBER_API_KEY_SCOPES, 'user:create'],
|
||||
});
|
||||
const apiKeyId = adminUser.apiKeys[0].id;
|
||||
|
||||
await publicApiKeyService.removeOwnerOnlyScopesFromApiKeys(adminUser);
|
||||
|
||||
const apiKeyOnDb = await apiKeyRepository.findOneByOrFail({ id: apiKeyId });
|
||||
expect(apiKeyOnDb.scopes).toEqual(MEMBER_API_KEY_SCOPES);
|
||||
});
|
||||
});
|
||||
|
||||
describe('apiKeyHasValidScopes', () => {
|
||||
|
|
@ -145,25 +161,14 @@ describe('PublicApiKeyService', () => {
|
|||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should let a member grant folder scopes, which apply to their own projects', async () => {
|
||||
// Arrange
|
||||
const folderScopes: ApiKeyScope[] = [
|
||||
'folder:create',
|
||||
'folder:read',
|
||||
'folder:update',
|
||||
'folder:delete',
|
||||
'folder:list',
|
||||
];
|
||||
|
||||
// Act
|
||||
it('should let a member grant every member scope', async () => {
|
||||
const result = publicApiKeyService.apiKeyHasValidScopesForRole(
|
||||
{
|
||||
role: GLOBAL_MEMBER_ROLE,
|
||||
},
|
||||
folderScopes,
|
||||
MEMBER_API_KEY_SCOPES,
|
||||
);
|
||||
|
||||
// Assert
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -352,6 +352,39 @@ describe('Variables in Public API', () => {
|
|||
);
|
||||
});
|
||||
|
||||
it('should let a member create a variable in a project they can edit', async () => {
|
||||
testServer.license.enable('feat:variables');
|
||||
const member = await createMemberWithApiKey({ scopes: ['variable:create'] });
|
||||
await linkUserToProject(member, project, 'project:editor');
|
||||
|
||||
const response = await testServer
|
||||
.publicApiAgentFor(member)
|
||||
.post('/variables')
|
||||
.send({ key: 'key', value: 'value', projectId: project.id });
|
||||
|
||||
expect(response.status).toBe(201);
|
||||
const created = await getVariableByKey('key');
|
||||
expect(created).not.toBeNull();
|
||||
await expect(getVariableByIdOrFail(created!.id)).resolves.toEqual(
|
||||
expect.objectContaining({ project: expect.objectContaining({ id: project.id }) }),
|
||||
);
|
||||
});
|
||||
|
||||
it('should not let a member create a global variable', async () => {
|
||||
testServer.license.enable('feat:variables');
|
||||
const member = await createMemberWithApiKey({ scopes: ['variable:create'] });
|
||||
await linkUserToProject(member, project, 'project:admin');
|
||||
|
||||
const response = await testServer
|
||||
.publicApiAgentFor(member)
|
||||
.post('/variables')
|
||||
.send({ key: 'key', value: 'value' });
|
||||
|
||||
expect(response.status).toBe(403);
|
||||
expect(response.body).toHaveProperty('message', 'You are not allowed to create a variable');
|
||||
await expect(getVariableByKey('key')).resolves.toBeNull();
|
||||
});
|
||||
|
||||
it('if not licensed, should reject', async () => {
|
||||
const variablePayload = { key: 'key', value: 'value' };
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user