1
0
mirror of https://github.com/Unleash/unleash.git synced 2026-01-05 20:06:22 +01:00

fix: filter project tokens based on permissions

This commit is contained in:
David Leek 2025-12-02 13:33:54 +01:00
parent 86c5c2ded5
commit 78c6c67a2b
No known key found for this signature in database
GPG Key ID: 515EE0F1BB6D0BE1
2 changed files with 135 additions and 2 deletions

View File

@ -9,6 +9,7 @@ import {
DELETE_FRONTEND_API_TOKEN,
READ_CLIENT_API_TOKEN,
READ_FRONTEND_API_TOKEN,
READ_PROJECT_API_TOKEN,
UPDATE_CLIENT_API_TOKEN,
UPDATE_FRONTEND_API_TOKEN,
} from '../../types/permissions.js';
@ -80,6 +81,7 @@ const canReadToken = ({ permission }: IUserPermission, type: ApiTokenType) => {
if (type === ApiTokenType.CLIENT || type === ApiTokenType.BACKEND) {
return [
CREATE_CLIENT_API_TOKEN,
READ_PROJECT_API_TOKEN,
READ_CLIENT_API_TOKEN,
DELETE_CLIENT_API_TOKEN,
UPDATE_CLIENT_API_TOKEN,
@ -419,10 +421,15 @@ export class ApiTokenController extends Controller {
const userPermissions =
await this.accessService.getPermissionsForUser(user);
console.log(JSON.stringify(userPermissions));
const accessibleTokens = allTokens.filter((token) =>
userPermissions.some((permission) =>
canReadToken(permission, token.type),
userPermissions.some(
(permission) =>
(token.project === permission.project ||
(permission.project &&
token.projects?.includes(permission.project))) &&
canReadToken(permission, token.type),
),
);
return accessibleTokens;

View File

@ -0,0 +1,126 @@
import {
type IUnleashTest,
setupAppWithAuth,
} from '../../helpers/test-helper.js';
import dbInit, { type ITestDb } from '../../helpers/database-init.js';
import getLogger from '../../../fixtures/no-logger.js';
import { ApiTokenType } from '../../../../lib/types/model.js';
import { DEFAULT_ENV } from '../../../../lib/util/index.js';
import {
SYSTEM_USER_AUDIT,
SYSTEM_USER_ID,
} from '../../../../lib/server-impl.js';
let db: ITestDb;
let app: IUnleashTest;
beforeAll(async () => {
db = await dbInit('token_api_project_token_filtering', getLogger);
app = await setupAppWithAuth(
db.stores,
{
experimental: {
flags: {
strictSchemaValidation: true,
},
},
},
db.rawDatabase,
);
});
afterAll(async () => {
if (db) {
await db.destroy();
}
await app.destroy();
});
afterEach(async () => {
await db.stores.apiTokenStore.deleteAll();
});
test('finds tokens and stuff', async () => {
const tokenSecret = 'random-secret';
const tokenSecretNotShown = 'other-random-secret';
const project_access = await db.stores.projectStore.create({
id: 'other',
name: 'other',
description: 'other',
mode: 'open',
});
const project_no_access = await db.stores.projectStore.create({
id: 'no_access',
name: 'no_access',
description: 'no_access',
mode: 'protected',
});
const token_shown = await db.stores.apiTokenStore.insert({
tokenName: 'test',
secret: tokenSecret,
type: ApiTokenType.BACKEND,
environment: DEFAULT_ENV,
projects: ['default', project_access.id, project_no_access.id],
});
const token_not_shown = await db.stores.apiTokenStore.insert({
tokenName: 'test_not_shown',
secret: tokenSecretNotShown,
type: ApiTokenType.BACKEND,
environment: DEFAULT_ENV,
projects: [project_no_access.id],
});
const projectMember = await app.services.userService.createUser({
username: 'custom',
name: 'Custom',
email: 'custom@getunleash.io',
rootRole: 2,
});
const customRole = await app.services.accessService.createRole(
{
name: 'Project api token reader',
description: '',
permissions: [
{
id: 42, // "READ_PROJECT_API_TOKEN"
},
],
createdByUserId: SYSTEM_USER_ID,
},
SYSTEM_USER_AUDIT,
);
await app.services.projectService.addAccess(
project_access.id,
[customRole.id],
[], // no groups
[projectMember.id],
SYSTEM_USER_AUDIT,
);
await app.login({
email: projectMember.email!,
});
const result = await app.request
.get(`/api/admin/api-tokens`)
.set('Content-Type', 'application/json')
.expect(200);
expect(result.body.tokens.length).toBe(1);
expect(result.body).toMatchObject({
tokens: [
{
secret: tokenSecret,
tokenName: token_shown.tokenName,
type: 'client',
projects: token_shown.projects,
},
],
});
});