mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-20 00:08:02 +01:00
fix: project tokens can now be created with the correct permissions (#4165)
This commit is contained in:
parent
d7b7d93533
commit
79dd508485
@ -7,6 +7,7 @@ import {
|
|||||||
ADMIN,
|
ADMIN,
|
||||||
CREATE_FRONTEND_API_TOKEN,
|
CREATE_FRONTEND_API_TOKEN,
|
||||||
CREATE_CLIENT_API_TOKEN,
|
CREATE_CLIENT_API_TOKEN,
|
||||||
|
CREATE_PROJECT_API_TOKEN,
|
||||||
} from '@server/types/permissions';
|
} from '@server/types/permissions';
|
||||||
import { useHasRootAccess } from 'hooks/useHasAccess';
|
import { useHasRootAccess } from 'hooks/useHasAccess';
|
||||||
import { SelectOption } from './TokenTypeSelector/TokenTypeSelector';
|
import { SelectOption } from './TokenTypeSelector/TokenTypeSelector';
|
||||||
@ -17,17 +18,28 @@ export const useApiTokenForm = (project?: string) => {
|
|||||||
const { uiConfig } = useUiConfig();
|
const { uiConfig } = useUiConfig();
|
||||||
const initialEnvironment = environments?.find(e => e.enabled)?.name;
|
const initialEnvironment = environments?.find(e => e.enabled)?.name;
|
||||||
|
|
||||||
|
const hasCreateTokenPermission = useHasRootAccess(CREATE_CLIENT_API_TOKEN);
|
||||||
|
const hasCreateProjectTokenPermission = useHasRootAccess(
|
||||||
|
CREATE_PROJECT_API_TOKEN,
|
||||||
|
project
|
||||||
|
);
|
||||||
|
|
||||||
const apiTokenTypes: SelectOption[] = [
|
const apiTokenTypes: SelectOption[] = [
|
||||||
{
|
{
|
||||||
key: TokenType.CLIENT,
|
key: TokenType.CLIENT,
|
||||||
label: `Server-side SDK (${TokenType.CLIENT})`,
|
label: `Server-side SDK (${TokenType.CLIENT})`,
|
||||||
title: 'Connect server-side SDK or Unleash Proxy',
|
title: 'Connect server-side SDK or Unleash Proxy',
|
||||||
enabled: useHasRootAccess(CREATE_CLIENT_API_TOKEN),
|
enabled:
|
||||||
|
hasCreateTokenPermission || hasCreateProjectTokenPermission,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const hasAdminAccess = useHasRootAccess(ADMIN);
|
const hasAdminAccess = useHasRootAccess(ADMIN);
|
||||||
const hasCreateFrontendAccess = useHasRootAccess(CREATE_FRONTEND_API_TOKEN);
|
const hasCreateFrontendAccess = useHasRootAccess(CREATE_FRONTEND_API_TOKEN);
|
||||||
|
const hasCreateFrontendTokenAccess = useHasRootAccess(
|
||||||
|
CREATE_PROJECT_API_TOKEN,
|
||||||
|
project
|
||||||
|
);
|
||||||
if (!project) {
|
if (!project) {
|
||||||
apiTokenTypes.push({
|
apiTokenTypes.push({
|
||||||
key: TokenType.ADMIN,
|
key: TokenType.ADMIN,
|
||||||
@ -42,7 +54,7 @@ export const useApiTokenForm = (project?: string) => {
|
|||||||
key: TokenType.FRONTEND,
|
key: TokenType.FRONTEND,
|
||||||
label: `Client-side SDK (${TokenType.FRONTEND})`,
|
label: `Client-side SDK (${TokenType.FRONTEND})`,
|
||||||
title: 'Connect web and mobile SDK directly to Unleash',
|
title: 'Connect web and mobile SDK directly to Unleash',
|
||||||
enabled: hasCreateFrontendAccess,
|
enabled: hasCreateFrontendAccess || hasCreateFrontendTokenAccess,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,7 +34,6 @@ import { Response } from 'express';
|
|||||||
import { timingSafeEqual } from 'crypto';
|
import { timingSafeEqual } from 'crypto';
|
||||||
import { createApiToken } from '../../../schema/api-token-schema';
|
import { createApiToken } from '../../../schema/api-token-schema';
|
||||||
import { OperationDeniedError } from '../../../error';
|
import { OperationDeniedError } from '../../../error';
|
||||||
import { tokenTypeToCreatePermission } from '../api-token';
|
|
||||||
|
|
||||||
interface ProjectTokenParam {
|
interface ProjectTokenParam {
|
||||||
token: string;
|
token: string;
|
||||||
@ -159,9 +158,7 @@ export class ProjectApiTokenController extends Controller {
|
|||||||
): Promise<any> {
|
): Promise<any> {
|
||||||
const createToken = await createApiToken.validateAsync(req.body);
|
const createToken = await createApiToken.validateAsync(req.body);
|
||||||
const { projectId } = req.params;
|
const { projectId } = req.params;
|
||||||
const permissionRequired = tokenTypeToCreatePermission(
|
const permissionRequired = CREATE_PROJECT_API_TOKEN;
|
||||||
createToken.type,
|
|
||||||
);
|
|
||||||
const hasPermission = await this.accessService.hasPermission(
|
const hasPermission = await this.accessService.hasPermission(
|
||||||
req.user,
|
req.user,
|
||||||
permissionRequired,
|
permissionRequired,
|
||||||
|
@ -5,6 +5,7 @@ import { ApiTokenType } from '../../../../lib/types/models/api-token';
|
|||||||
import { RoleName } from '../../../../lib/types/model';
|
import { RoleName } from '../../../../lib/types/model';
|
||||||
import {
|
import {
|
||||||
CREATE_CLIENT_API_TOKEN,
|
CREATE_CLIENT_API_TOKEN,
|
||||||
|
CREATE_PROJECT_API_TOKEN,
|
||||||
DELETE_CLIENT_API_TOKEN,
|
DELETE_CLIENT_API_TOKEN,
|
||||||
READ_CLIENT_API_TOKEN,
|
READ_CLIENT_API_TOKEN,
|
||||||
READ_FRONTEND_API_TOKEN,
|
READ_FRONTEND_API_TOKEN,
|
||||||
@ -171,6 +172,60 @@ test('Token-admin should be allowed to create token', async () => {
|
|||||||
await destroy();
|
await destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('A role with only CREATE_PROJECT_API_TOKEN can create project tokens', async () => {
|
||||||
|
expect.assertions(0);
|
||||||
|
|
||||||
|
const preHook = (app, config, { userService, accessService }) => {
|
||||||
|
app.use('/api/admin/', async (req, res, next) => {
|
||||||
|
const role = await accessService.getRootRole(RoleName.VIEWER);
|
||||||
|
const user = await userService.createUser({
|
||||||
|
email: 'powerpuffgirls_viewer@example.com',
|
||||||
|
rootRole: role.id,
|
||||||
|
});
|
||||||
|
req.user = user;
|
||||||
|
const createClientApiTokenRole = await accessService.createRole({
|
||||||
|
name: 'project_client_token_creator',
|
||||||
|
description: 'Can create client tokens',
|
||||||
|
permissions: [],
|
||||||
|
type: 'root-custom',
|
||||||
|
});
|
||||||
|
await accessService.addPermissionToRole(
|
||||||
|
role.id,
|
||||||
|
CREATE_PROJECT_API_TOKEN,
|
||||||
|
);
|
||||||
|
await accessService.addUserToRole(
|
||||||
|
user.id,
|
||||||
|
createClientApiTokenRole.id,
|
||||||
|
'default',
|
||||||
|
);
|
||||||
|
req.user = await userService.createUser({
|
||||||
|
email: 'someguyinplaces@example.com',
|
||||||
|
rootRole: role.id,
|
||||||
|
});
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const { request, destroy } = await setupAppWithCustomAuth(stores, preHook, {
|
||||||
|
experimental: {
|
||||||
|
flags: {
|
||||||
|
customRootRoles: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
await request
|
||||||
|
.post('/api/admin/projects/default/api-tokens')
|
||||||
|
.send({
|
||||||
|
username: 'client-token-maker',
|
||||||
|
type: 'client',
|
||||||
|
projects: ['default'],
|
||||||
|
})
|
||||||
|
.set('Content-Type', 'application/json')
|
||||||
|
.expect(201);
|
||||||
|
await destroy();
|
||||||
|
});
|
||||||
|
|
||||||
describe('Fine grained API token permissions', () => {
|
describe('Fine grained API token permissions', () => {
|
||||||
describe('A role with access to CREATE_CLIENT_API_TOKEN', () => {
|
describe('A role with access to CREATE_CLIENT_API_TOKEN', () => {
|
||||||
test('should be allowed to create client tokens', async () => {
|
test('should be allowed to create client tokens', async () => {
|
||||||
|
Loading…
Reference in New Issue
Block a user