From 8ba36ee9a2df107340357a86faea0268b0f9c010 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gast=C3=B3n=20Fournier?= Date: Thu, 28 Aug 2025 00:02:56 -0700 Subject: [PATCH] fix: consider both client and backend token types from the DB (#10552) ## About the changes In the previous fix: https://github.com/Unleash/unleash/pull/10543, we made sure client token types were displayed in the UI. Here, we're also making sure that the Backend token types are displayed as well. 1. A test validates that if a backend token exists in the db it will be returned in the API response. 2. The UI has been adapted to also consider backend token types --- .../common/ApiTokenTable/useApiTokenTable.tsx | 6 +- src/lib/routes/admin-api/api-token.ts | 69 +- .../e2e/api/admin/api-token.auth.e2e.test.ts | 1048 ++++------------- 3 files changed, 248 insertions(+), 875 deletions(-) diff --git a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx index 0267784412..06a3f4c5b9 100644 --- a/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx +++ b/frontend/src/component/common/ApiTokenTable/useApiTokenTable.tsx @@ -49,7 +49,7 @@ export const useApiTokenTable = ( Cell: ({ value, }: { - value: 'client' | 'admin' | 'frontend'; + value: 'client' | 'backend' | 'admin' | 'frontend'; }) => ( string = } }; -const permissionToTokenType: (permission: string) => ApiTokenType | undefined = - (permission) => { - if ( - [ - CREATE_FRONTEND_API_TOKEN, - READ_FRONTEND_API_TOKEN, - DELETE_FRONTEND_API_TOKEN, - UPDATE_FRONTEND_API_TOKEN, - ].includes(permission) - ) { - return ApiTokenType.FRONTEND; - } else if ( - [ - CREATE_CLIENT_API_TOKEN, - READ_CLIENT_API_TOKEN, - DELETE_CLIENT_API_TOKEN, - UPDATE_CLIENT_API_TOKEN, - ].includes(permission) - ) { - return ApiTokenType.CLIENT; - } else if (ADMIN === permission) { - return ApiTokenType.ADMIN; - } else { - return undefined; - } - }; +const canReadToken = ({ permission }: IUserPermission, type: ApiTokenType) => { + if (permission === ADMIN) { + return true; + } + if (type === ApiTokenType.FRONTEND) { + return [ + CREATE_FRONTEND_API_TOKEN, + READ_FRONTEND_API_TOKEN, + DELETE_FRONTEND_API_TOKEN, + UPDATE_FRONTEND_API_TOKEN, + ].includes(permission); + } + if (type === ApiTokenType.CLIENT || type === ApiTokenType.BACKEND) { + return [ + CREATE_CLIENT_API_TOKEN, + READ_CLIENT_API_TOKEN, + DELETE_CLIENT_API_TOKEN, + UPDATE_CLIENT_API_TOKEN, + ].includes(permission); + } + return false; +}; const tokenTypeToUpdatePermission: (tokenType: ApiTokenType) => string = ( tokenType, @@ -419,23 +416,15 @@ export class ApiTokenController extends Controller { if (user.isAPI && user.permissions.includes(ADMIN)) { return allTokens; } + const userPermissions = await this.accessService.getPermissionsForUser(user); - const allowedTokenTypes = [ - ADMIN, - READ_CLIENT_API_TOKEN, - READ_FRONTEND_API_TOKEN, - ] - .filter((readPerm) => - userPermissions.some( - (p) => p.permission === readPerm || p.permission === ADMIN, - ), - ) - .map(permissionToTokenType) - .filter((t) => t); - return allTokens.filter((token) => - allowedTokenTypes.includes(token.type), + const accessibleTokens = allTokens.filter((token) => + userPermissions.some((permission) => + canReadToken(permission, token.type), + ), ); + return accessibleTokens; } } diff --git a/src/test/e2e/api/admin/api-token.auth.e2e.test.ts b/src/test/e2e/api/admin/api-token.auth.e2e.test.ts index cb423853e3..16b6829e5c 100644 --- a/src/test/e2e/api/admin/api-token.auth.e2e.test.ts +++ b/src/test/e2e/api/admin/api-token.auth.e2e.test.ts @@ -1,4 +1,7 @@ -import { setupAppWithCustomAuth } from '../../helpers/test-helper.js'; +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'; @@ -7,27 +10,94 @@ import { CREATE_CLIENT_API_TOKEN, CREATE_PROJECT_API_TOKEN, DELETE_CLIENT_API_TOKEN, + type IApiToken, type IUnleashStores, READ_CLIENT_API_TOKEN, READ_FRONTEND_API_TOKEN, - SYSTEM_USER, SYSTEM_USER_AUDIT, SYSTEM_USER_ID, UPDATE_CLIENT_API_TOKEN, } from '../../../../lib/types/index.js'; import { addDays } from 'date-fns'; -import type { - AccessService, - IUnleashServices, - UserService, -} from '../../../../lib/services/index.js'; +import type { PermissionRef } from '../../../../lib/services/access-service.js'; let stores: IUnleashStores; let db: ITestDb; +let app: IUnleashTest; +let adminToken: IApiToken; +let frontendToken: IApiToken; +let clientToken: IApiToken; +let backendToken: IApiToken; + +const setupUser = async ( + email: string, + roleName: RoleName, + permissions?: PermissionRef[], +) => { + const { accessService, userService } = app.services; + const role = (await accessService.getPredefinedRole(roleName))!; + const user = await userService.createUser( + { + email, + rootRole: role.id, + }, + SYSTEM_USER_AUDIT, + ); + if (permissions) { + const createClientApiTokenRole = await accessService.createRole( + { + name: `project_client_${email}`, + description: `${email} role`, + permissions, + type: 'root-custom', + createdByUserId: SYSTEM_USER_ID, + }, + SYSTEM_USER_AUDIT, + ); + await accessService.addUserToRole( + user.id, + createClientApiTokenRole.id, + 'default', + ); + } +}; beforeAll(async () => { db = await dbInit('token_api_auth_serial', getLogger); stores = db.stores; + app = await setupAppWithAuth(stores, {}, db.rawDatabase); + + // insert initial tokens + clientToken = await stores.apiTokenStore.insert({ + environment: '', + projects: [], + + tokenName: 'client', + secret: '*:environment.client_secret', + type: ApiTokenType.CLIENT, + }); + backendToken = await stores.apiTokenStore.insert({ + environment: '', + projects: [], + + tokenName: 'backend', + secret: '*:environment.backend_secret', + type: ApiTokenType.BACKEND, + }); + adminToken = await stores.apiTokenStore.insert({ + environment: '', + projects: [], + tokenName: 'admin', + secret: '*:*.sdfsdf2admin_secret', + type: ApiTokenType.ADMIN, + }); + frontendToken = await stores.apiTokenStore.insert({ + environment: '', + projects: [], + tokenName: 'frontender', + secret: '*:environment:sdfsdf2dfrontend_Secret', + type: ApiTokenType.FRONTEND, + }); }); afterAll(async () => { @@ -36,167 +106,45 @@ afterAll(async () => { } }); -afterEach(async () => { - await stores.apiTokenStore.deleteAll(); -}); +test('editor users should only get client, backend or frontend tokens', async () => { + await setupUser('editor@example.com', RoleName.EDITOR, [ + { name: READ_CLIENT_API_TOKEN }, + { name: READ_FRONTEND_API_TOKEN }, + ]); + await app.login({ email: 'editor@example.com' }); -test('editor users should only get client or frontend tokens', async () => { - expect.assertions(3); - - const preHook = (app, config, { userService, accessService }) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole(RoleName.EDITOR); - const user = await userService.createUser({ - email: 'editor@example.com', - rootRole: role.id, - }); - req.user = user; - next(); - }); - }; - - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, - ); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'test', - secret: '*:environment.1234', - type: ApiTokenType.CLIENT, - }); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontend', - secret: '*:environment.12345', - type: ApiTokenType.FRONTEND, - }); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'test', - secret: '*:*.sdfsdf2d', - type: ApiTokenType.ADMIN, - }); - - await request + await app.request .get('/api/admin/api-tokens') .expect('Content-Type', /json/) .expect(200) .expect((res) => { - expect(res.body.tokens.length).toBe(2); + expect(res.body.tokens.length).toBe(3); expect(res.body.tokens[0].type).toBe(ApiTokenType.CLIENT); expect(res.body.tokens[1].type).toBe(ApiTokenType.FRONTEND); + expect(res.body.tokens[2].type).toBe(ApiTokenType.BACKEND); }); - - await destroy(); }); test('viewer users should not be allowed to fetch tokens', async () => { - expect.assertions(0); - - const preHook = (app, config, { userService, accessService }) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole(RoleName.VIEWER); - const user = await userService.createUser({ - email: 'viewer@example.com', - rootRole: role.id, - }); - req.user = user; - next(); - }); - }; - - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, - ); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'test', - secret: '*:environment.1234', - type: ApiTokenType.CLIENT, - }); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'test', - secret: '*:*.sdfsdf2d', - type: ApiTokenType.ADMIN, - }); - - await request + await setupUser('viewer@example.com', RoleName.VIEWER); + await app.login({ email: 'viewer@example.com' }); + await app.request .get('/api/admin/api-tokens') .expect('Content-Type', /json/) .expect(403); - - await destroy(); }); test.each(['client', 'backend'])( 'A role with only CREATE_PROJECT_API_TOKEN can create project %s token', async (type) => { - expect.assertions(1); - - const preHook = ( - app, - config, - { - userService, - accessService, - }: { userService: UserService; accessService: AccessService }, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = (await accessService.getPredefinedRole( - RoleName.VIEWER, - ))!; - const user = await userService.createUser( - { - email: `powerpuffgirls_viewer_${type}@example.com`, - rootRole: role.id, - }, - SYSTEM_USER_AUDIT, - ); - const createClientApiTokenRole = await accessService.createRole( - { - name: `project_client_${type}_token_creator`, - description: `Can create ${type} tokens`, - permissions: [{ name: CREATE_PROJECT_API_TOKEN }], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addUserToRole( - user.id, - createClientApiTokenRole.id, - 'default', - ); - req.user = user; - next(); - }); - }; - - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - {}, - db.rawDatabase, + await setupUser( + `powerpuffgirls_viewer_${type}@example.com`, + RoleName.VIEWER, + [{ name: CREATE_PROJECT_API_TOKEN }], ); - const { body, status } = await request + await app.login({ email: `powerpuffgirls_viewer_${type}@example.com` }); + const { body, status } = await app.request .post('/api/admin/projects/default/api-tokens') .send({ tokenName: `${type}-token-maker`, @@ -204,63 +152,23 @@ test.each(['client', 'backend'])( projects: ['default'], }) .set('Content-Type', 'application/json'); - console.log(`Response: ${JSON.stringify(body)}`); + expect(status).toBe(201); - await destroy(); + // clean up + await stores.apiTokenStore.delete(body.secret); }, ); describe('Fine grained API token permissions', () => { describe('A role with access to CREATE_CLIENT_API_TOKEN', () => { test('should be allowed to create client tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const builtInRole = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'mylittlepony_viewer@example.com', - rootRole: builtInRole.id, - }); - req.user = user; - const createClientApiTokenRole = - await accessService.createRole( - { - name: 'client_token_creator', - description: 'Can create client tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER.id, - }, - SYSTEM_USER_AUDIT, - ); - // not sure if we should add the permission to the builtin role or to the newly created role - await accessService.addPermissionToRole( - builtInRole.id, - CREATE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - createClientApiTokenRole.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'mylittlepony_viewer@example.com', + RoleName.VIEWER, + [{ name: CREATE_CLIENT_API_TOKEN }], ); - await request + await app.login({ email: 'mylittlepony_viewer@example.com' }); + const { body } = await app.request .post('/api/admin/api-tokens') .send({ tokenName: 'default-client', @@ -268,56 +176,20 @@ describe('Fine grained API token permissions', () => { }) .set('Content-Type', 'application/json') .expect(201); - await destroy(); + + // clean up + await stores.apiTokenStore.delete(body.secret); }); test('should NOT be allowed to create frontend tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'mylittlepony_viewer_frontend@example.com', - rootRole: role.id, - }); - req.user = user; - const createClientApiTokenRole = - await accessService.createRole( - { - name: 'client_token_creator_cannot_create_frontend', - description: 'Can create client tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - role.id, - CREATE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - createClientApiTokenRole.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'mylittlepony_viewer_frontend@example.com', + RoleName.VIEWER, + [{ name: CREATE_CLIENT_API_TOKEN }], ); - await request + await app.login({ + email: 'mylittlepony_viewer_frontend@example.com', + }); + await app.request .post('/api/admin/api-tokens') .send({ tokenName: 'default-frontend', @@ -325,660 +197,168 @@ describe('Fine grained API token permissions', () => { }) .set('Content-Type', 'application/json') .expect(403); - await destroy(); }); }); describe('Read operations', () => { test('READ_FRONTEND_API_TOKEN should be able to see FRONTEND tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'read_frontend_token@example.com', - rootRole: role.id, - }); - req.user = user; - const readFrontendApiToken = await accessService.createRole( - { - name: 'frontend_token_reader', - description: 'Can read frontend tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - readFrontendApiToken.id, - READ_FRONTEND_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - readFrontendApiToken.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'read_frontend_token@example.com', + RoleName.VIEWER, + [{ name: READ_FRONTEND_API_TOKEN }], ); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], + await app.login({ email: 'read_frontend_token@example.com' }); - tokenName: 'client', - secret: '*:environment.client_secret', - type: ApiTokenType.CLIENT, - }); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'admin', - secret: '*:*.sdfsdf2admin_secret', - type: ApiTokenType.ADMIN, - }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontender', - secret: '*:environment:sdfsdf2dfrontend_Secret', - type: ApiTokenType.FRONTEND, - }); - await request + const { body, status } = await app.request .get('/api/admin/api-tokens') - .set('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect( - res.body.tokens.every( - (t) => t.type === ApiTokenType.FRONTEND, - ), - ).toBe(true); - }); - await destroy(); + .set('Content-Type', 'application/json'); + expect(status).toBe(200); + expect( + body.tokens.every((t) => t.type === ApiTokenType.FRONTEND), + ).toBe(true); }); - test('READ_CLIENT_API_TOKEN should be able to see CLIENT tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'read_client_token@example.com', - rootRole: role.id, - }); - req.user = user; - const readClientTokenRole = await accessService.createRole( - { - name: 'client_token_reader', - description: 'Can read client tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - readClientTokenRole.id, - READ_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - readClientTokenRole.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, - ); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'client', - secret: '*:environment.client_secret_1234', - type: ApiTokenType.CLIENT, - }); + test('READ_CLIENT_API_TOKEN should be able to see CLIENT and BACKEND tokens', async () => { + await setupUser('read_client_token@example.com', RoleName.VIEWER, [ + { name: READ_CLIENT_API_TOKEN }, + ]); + await app.login({ email: 'read_client_token@example.com' }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'admin', - secret: '*:*.admin_secret_1234', - type: ApiTokenType.ADMIN, - }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontender', - secret: '*:environment.frontend_secret_1234', - type: ApiTokenType.FRONTEND, - }); - await request - .get('/api/admin/api-tokens') - .set('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body.tokens).toHaveLength(1); - expect(res.body.tokens[0].type).toBe(ApiTokenType.CLIENT); - }); - await destroy(); - }); - test('Admin users should be able to see all tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.ADMIN, - ); - const user = await userService.createUser({ - email: 'read_admin_token@example.com', - rootRole: role.id, - }); - req.user = user; - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, - ); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'client', - secret: '*:environment.client_secret_4321', - type: ApiTokenType.CLIENT, - }); - - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'admin', - secret: '*:*.admin_secret_4321', - type: ApiTokenType.ADMIN, - }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontender', - secret: '*:environment.frontend_secret_4321', - type: ApiTokenType.FRONTEND, - }); - await request - .get('/api/admin/api-tokens') - .set('Content-Type', 'application/json') - .expect(200) - .expect((res) => { - expect(res.body.tokens).toHaveLength(3); - }); - await destroy(); - }); - test('Editor users should be able to see all tokens except ADMIN tokens', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.EDITOR, - ); - const user = await userService.createUser({ - email: 'standard-editor-reads-tokens@example.com', - rootRole: role.id, - }); - req.user = user; - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, - ); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'client', - secret: '*:environment.client_secret_4321', - type: ApiTokenType.CLIENT, - }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'admin', - secret: '*:*.admin_secret_4321', - type: ApiTokenType.ADMIN, - }); - await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontender', - secret: '*:environment.frontend_secret_4321', - type: ApiTokenType.FRONTEND, - }); - await request + await app.request .get('/api/admin/api-tokens') .set('Content-Type', 'application/json') .expect(200) .expect((res) => { expect(res.body.tokens).toHaveLength(2); + expect(res.body.tokens[0].type).toBe(ApiTokenType.CLIENT); + expect(res.body.tokens[1].type).toBe(ApiTokenType.BACKEND); + }); + }); + test('Admin users should be able to see all tokens', async () => { + await setupUser('read_admin_token@example.com', RoleName.ADMIN); + await app.login({ email: 'read_admin_token@example.com' }); + const { body, status } = await app.request + .get('/api/admin/api-tokens') + .set('Content-Type', 'application/json'); + expect(status).toBe(200); + expect(body.tokens).toHaveLength(4); + [ + ApiTokenType.ADMIN, + ApiTokenType.CLIENT, + ApiTokenType.BACKEND, + ApiTokenType.FRONTEND, + ].forEach((tokenType) => { + expect( + body.tokens.filter( + (t: { type: string }) => t.type === tokenType, + ), + ).toHaveLength(1); + }); + }); + test('Editor users should be able to see all tokens except ADMIN tokens', async () => { + await setupUser( + 'standard-editor-reads-tokens@example.com', + RoleName.EDITOR, + ); + await app.login({ + email: 'standard-editor-reads-tokens@example.com', + }); + await app.request + .get('/api/admin/api-tokens') + .set('Content-Type', 'application/json') + .expect(200) + .expect((res) => { + expect(res.body.tokens).toHaveLength(3); expect( res.body.tokens.filter( ({ type }) => type === ApiTokenType.ADMIN, ), ).toHaveLength(0); }); - await destroy(); }); }); describe('Update operations', () => { describe('UPDATE_CLIENT_API_TOKEN can', () => { test('UPDATE client_api token expiry', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'update_client_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'update_client_token', - description: 'Can update client tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - UPDATE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'update_client_token@example.com', + RoleName.VIEWER, + [{ name: UPDATE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'cilent', - secret: '*:environment.update_client_token', - type: ApiTokenType.CLIENT, - }); - await request - .put(`/api/admin/api-tokens/${token.secret}`) + await app.login({ email: 'update_client_token@example.com' }); + await app.request + .put(`/api/admin/api-tokens/${clientToken.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(200); - await destroy(); }); test('NOT UPDATE frontend_api token expiry', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'update_frontend_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'update_client_token_not_frontend', - description: - 'Can not update frontend tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - UPDATE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'update_frontend_token@example.com', + RoleName.VIEWER, + [{ name: UPDATE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontend', - secret: '*:environment.update_frontend_token', - type: ApiTokenType.FRONTEND, - }); - await request - .put(`/api/admin/api-tokens/${token.secret}`) + await app.login({ email: 'update_frontend_token@example.com' }); + await app.request + .put(`/api/admin/api-tokens/${frontendToken.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(403); - - await destroy(); }); test('NOT UPDATE admin_api token expiry', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'update_admin_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'update_client_token_not_admin', - description: 'Can not update admin tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - UPDATE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'update_admin_token@example.com', + RoleName.VIEWER, + [{ name: UPDATE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ - environment: '', - projects: [], - - tokenName: 'admin', - secret: '*:*.update_admin_token', - type: ApiTokenType.ADMIN, - }); - await request - .put(`/api/admin/api-tokens/${token.secret}`) + await app.request + .put(`/api/admin/api-tokens/${adminToken.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(403); - await destroy(); }); }); }); describe('Delete operations', () => { describe('DELETE_CLIENT_API_TOKEN can', () => { test('DELETE client_api token', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'delete_client_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'delete_client_token', - description: 'Can delete client tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - DELETE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'delete_client_token@example.com', + RoleName.VIEWER, + [{ name: DELETE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ + await app.login({ email: 'delete_client_token@example.com' }); + const tokenToDelete = await stores.apiTokenStore.insert({ environment: '', projects: [], tokenName: 'cilent', secret: '*:environment.delete_client_token', type: ApiTokenType.CLIENT, }); - await request - .delete(`/api/admin/api-tokens/${token.secret}`) + await app.request + .delete(`/api/admin/api-tokens/${tokenToDelete.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(200); - await destroy(); }); test('NOT DELETE frontend_api token', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'delete_frontend_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'delete_client_token_not_frontend', - description: - 'Can not delete frontend tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - DELETE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'delete_frontend_token@example.com', + RoleName.VIEWER, + [{ name: DELETE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'frontend', - secret: '*:environment.delete_frontend_token', - type: ApiTokenType.FRONTEND, - }); - await request - .delete(`/api/admin/api-tokens/${token.secret}`) + await app.login({ email: 'delete_frontend_token@example.com' }); + await app.request + .delete(`/api/admin/api-tokens/${frontendToken.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(403); - await destroy(); }); test('NOT DELETE admin_api token', async () => { - const preHook = ( - app, - config, - { - userService, - accessService, - }: Pick, - ) => { - app.use('/api/admin/', async (req, res, next) => { - const role = await accessService.getPredefinedRole( - RoleName.VIEWER, - ); - const user = await userService.createUser({ - email: 'delete_admin_token@example.com', - rootRole: role.id, - }); - req.user = user; - const updateClientApiExpiry = - await accessService.createRole( - { - name: 'delete_client_token_not_admin', - description: 'Can not delete admin tokens', - permissions: [], - type: 'root-custom', - createdByUserId: SYSTEM_USER_ID, - }, - SYSTEM_USER_AUDIT, - ); - await accessService.addPermissionToRole( - updateClientApiExpiry.id, - DELETE_CLIENT_API_TOKEN, - ); - await accessService.addUserToRole( - user.id, - updateClientApiExpiry.id, - 'default', - ); - next(); - }); - }; - const { request, destroy } = await setupAppWithCustomAuth( - stores, - preHook, - undefined, - db.rawDatabase, + await setupUser( + 'delete_admin_token@example.com', + RoleName.VIEWER, + [{ name: DELETE_CLIENT_API_TOKEN }], ); - const token = await stores.apiTokenStore.insert({ - environment: '', - projects: [], - tokenName: 'admin', - secret: '*:*:delete_admin_token', - type: ApiTokenType.ADMIN, - }); - await request - .delete(`/api/admin/api-tokens/${token.secret}`) + await app.login({ email: 'delete_admin_token@example.com' }); + + await app.request + .delete(`/api/admin/api-tokens/${adminToken.secret}`) .send({ expiresAt: addDays(new Date(), 14) }) .expect(403); - await destroy(); }); }); });