1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-07 01:16:28 +02:00

wip: environment for permissions

This commit is contained in:
Ivar Conradi Østhus 2021-11-12 13:12:11 +01:00
parent cc66cc4adf
commit 2ec427190e
No known key found for this signature in database
GPG Key ID: 31AC596886B0BD09
6 changed files with 88 additions and 7 deletions

View File

@ -67,7 +67,7 @@ export class AccessStore implements IAccessStore {
async getPermissionsForUser(userId: number): Promise<IUserPermission[]> {
const stopTimer = this.timer('getPermissionsForUser');
const rows = await this.db
.select('project', 'permission')
.select('project', 'permission', 'environment')
.from<IUserPermission>(`${T.ROLE_PERMISSION} AS rp`)
.leftJoin(`${T.ROLE_USER} AS ur`, 'ur.role_id', 'rp.role_id')
.where('ur.user_id', '=', userId);
@ -78,7 +78,7 @@ export class AccessStore implements IAccessStore {
async getPermissionsForRole(roleId: number): Promise<IUserPermission[]> {
const stopTimer = this.timer('getPermissionsForRole');
const rows = await this.db
.select('project', 'permission')
.select('project', 'permission', 'environment')
.from<IUserPermission>(`${T.ROLE_PERMISSION}`)
.where('role_id', '=', roleId);
stopTimer();
@ -195,11 +195,13 @@ export class AccessStore implements IAccessStore {
role_id: number,
permissions: string[],
projectId?: string,
environment?: string,
): Promise<void> {
const rows = permissions.map((permission) => ({
role_id,
project: projectId,
permission,
environment,
}));
return this.db.batchInsert(T.ROLE_PERMISSION, rows);
}

View File

@ -19,6 +19,7 @@ import {
} from '../types/model';
export const ALL_PROJECTS = '*';
export const ALL_ENVS = '*';
const PROJECT_DESCRIPTION = {
OWNER: 'Users with this role have full control over the project, and can add and manage other users within the project context, manage feature toggles within the project, and control advanced project features like archiving and deleting the project.',
@ -81,9 +82,10 @@ export class AccessService {
user: User,
permission: string,
projectId?: string,
environment?: string,
): Promise<boolean> {
this.logger.info(
`Checking permission=${permission}, userId=${user.id} projectId=${projectId}`,
`Checking permission=${permission}, userId=${user.id}, projectId=${projectId}, environment=${environment}`,
);
try {
@ -96,6 +98,12 @@ export class AccessService {
p.project === projectId ||
p.project === ALL_PROJECTS,
)
.filter(
(p) =>
!p.environment ||
p.environment === environment ||
p.environment === ALL_ENVS,
)
.some(
(p) =>
p.permission === permission || p.permission === ADMIN,

View File

@ -1,4 +1,3 @@
// Special
export const ADMIN = 'ADMIN';
export const CLIENT = 'CLIENT';
export const NONE = 'NONE';
@ -6,21 +5,32 @@ export const NONE = 'NONE';
export const CREATE_FEATURE = 'CREATE_FEATURE';
export const UPDATE_FEATURE = 'UPDATE_FEATURE';
export const DELETE_FEATURE = 'DELETE_FEATURE';
export const CREATE_FEATURE_STRATEGY = 'CREATE_FEATURE_STRATEGY';
export const UPDATE_FEATURE_STRATEGY = 'UPDATE_FEATURE_STRATEGY';
export const DELETE_FEATURE_STRATEGY = 'DELETE_FEATURE_STRATEGY';
export const UPDATE_FEATURE_ENVIRONMENT = 'UPDATE_FEATURE_ENVIRONMENT';
export const CREATE_STRATEGY = 'CREATE_STRATEGY';
export const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
export const DELETE_STRATEGY = 'DELETE_STRATEGY';
export const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
export const CREATE_CONTEXT_FIELD = 'CREATE_CONTEXT_FIELD';
export const UPDATE_CONTEXT_FIELD = 'UPDATE_CONTEXT_FIELD';
export const DELETE_CONTEXT_FIELD = 'DELETE_CONTEXT_FIELD';
export const CREATE_PROJECT = 'CREATE_PROJECT';
export const UPDATE_PROJECT = 'UPDATE_PROJECT';
export const DELETE_PROJECT = 'DELETE_PROJECT';
export const CREATE_ADDON = 'CREATE_ADDON';
export const UPDATE_ADDON = 'UPDATE_ADDON';
export const DELETE_ADDON = 'DELETE_ADDON';
export const READ_ROLE = 'READ_ROLE';
export const UPDATE_ROLE = 'UPDATE_ROLE';
export const UPDATE_API_TOKEN = 'UPDATE_API_TOKEN';
export const CREATE_API_TOKEN = 'CREATE_API_TOKEN';
export const DELETE_API_TOKEN = 'DELETE_API_TOKEN';

View File

@ -2,6 +2,7 @@ import { Store } from './store';
export interface IUserPermission {
project?: string;
environment?: string;
permission: string;
}
@ -39,6 +40,7 @@ export interface IAccessStore extends Store<IRole, number> {
role_id: number,
permissions: string[],
projectId?: string,
environment?: string,
): Promise<void>;
removePermissionFromRole(
roleId: number,

View File

@ -0,0 +1,12 @@
'use strict';
exports.up = function (db, cb) {
db.runSql(
'ALTER TABLE role_permission ADD COLUMN environment varchar(255);',
cb,
);
};
exports.down = function (db, cb) {
db.runSql('ALTER TABLE role_permission DROP COLUMN environment', cb);
};

View File

@ -1,4 +1,4 @@
import dbInit from '../helpers/database-init';
import dbInit, { ITestDb } from '../helpers/database-init';
import getLogger from '../../fixtures/no-logger';
// eslint-disable-next-line import/no-unresolved
@ -9,9 +9,10 @@ import {
import * as permissions from '../../../lib/types/permissions';
import { RoleName } from '../../../lib/types/model';
import { IUnleashStores } from '../../../lib/types';
let db;
let stores;
let db: ITestDb;
let stores: IUnleashStores;
let accessService;
let editorUser;
@ -453,3 +454,49 @@ test('should not crash if user does not have permission', async () => {
expect(hasAccess).toBe(false);
});
test('should support permission with "ALL" environment requirement', async () => {
const { userStore, accessStore } = stores;
const user = await userStore.insert({
name: 'Some User',
email: 'randomEnv1@getunleash.io',
});
await accessService.setUserRootRole(user.id, readRole.id);
const customRole = await accessStore.createRole(
'Power user',
'custom',
'default',
'Grants access to modify all environments',
);
const { CREATE_FEATURE_STRATEGY } = permissions;
await accessStore.addPermissionsToRole(
customRole.id,
[CREATE_FEATURE_STRATEGY],
'default',
ALL_PROJECTS,
);
await accessStore.addUserToRole(user.id, customRole.id);
const hasAccess = await accessService.hasPermission(
user,
CREATE_FEATURE_STRATEGY,
'default',
'production',
);
expect(hasAccess).toBe(true);
const hasNotAccess = await accessService.hasPermission(
user,
CREATE_FEATURE_STRATEGY,
'default',
'development',
);
expect(hasNotAccess).toBe(true);
});