mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-31 00:16:47 +01:00
Use full name instead of perms, 403 error message now includes expected permission
This commit is contained in:
parent
f4a7aaa861
commit
d9804c0114
8
lib/missing-permission.js
Normal file
8
lib/missing-permission.js
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
module.exports = class MissingPermission {
|
||||||
|
constructor({ permission, message }) {
|
||||||
|
this.permission = permission;
|
||||||
|
this.message = message;
|
||||||
|
}
|
||||||
|
};
|
@ -12,6 +12,7 @@ const DEFAULT_OPTIONS = {
|
|||||||
baseUriPath: process.env.BASE_URI_PATH || '',
|
baseUriPath: process.env.BASE_URI_PATH || '',
|
||||||
serverMetrics: true,
|
serverMetrics: true,
|
||||||
enableLegacyRoutes: true,
|
enableLegacyRoutes: true,
|
||||||
|
extendedPermissions: false,
|
||||||
publicFolder,
|
publicFolder,
|
||||||
enableRequestLogger: isDev(),
|
enableRequestLogger: isDev(),
|
||||||
secret: 'UNLEASH-SECRET',
|
secret: 'UNLEASH-SECRET',
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
|
const MissingPermission = require('./missing-permission');
|
||||||
|
|
||||||
const ADMIN = 'ADMIN';
|
const ADMIN = 'ADMIN';
|
||||||
const CREATE_FEATURE = 'CREATE_FEATURE';
|
const CREATE_FEATURE = 'CREATE_FEATURE';
|
||||||
const UPDATE_FEATURE = 'UPDATE_FEATURE';
|
const UPDATE_FEATURE = 'UPDATE_FEATURE';
|
||||||
@ -9,29 +11,30 @@ const UPDATE_STRATEGY = 'UPDATE_STRATEGY';
|
|||||||
const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
const DELETE_STRATEGY = 'DELETE_STRATEGY';
|
||||||
const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
|
const UPDATE_APPLICATION = 'UPDATE_APPLICATION';
|
||||||
|
|
||||||
function requirePerms(prms) {
|
function requirePermission(permission) {
|
||||||
return (req, res, next) => {
|
return (req, res, next) => {
|
||||||
for (const permission of prms) {
|
if (
|
||||||
if (
|
req.user &&
|
||||||
req.user &&
|
req.user.permissions &&
|
||||||
req.user.permissions &&
|
(req.user.permissions.indexOf(ADMIN) !== -1 ||
|
||||||
(req.user.permissions.indexOf(ADMIN) !== -1 ||
|
req.user.permissions.indexOf(permission) !== -1)
|
||||||
req.user.permissions.indexOf(permission) !== -1)
|
) {
|
||||||
) {
|
return next();
|
||||||
return next();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
.status(403)
|
.status(403)
|
||||||
.json({
|
.json(
|
||||||
message: 'Missing permissions to perform this action.',
|
new MissingPermission({
|
||||||
})
|
permission,
|
||||||
|
message: `You require ${permission} to perform this action`,
|
||||||
|
})
|
||||||
|
)
|
||||||
.end();
|
.end();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
requirePerms,
|
requirePermission,
|
||||||
ADMIN,
|
ADMIN,
|
||||||
CREATE_FEATURE,
|
CREATE_FEATURE,
|
||||||
UPDATE_FEATURE,
|
UPDATE_FEATURE,
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
const test = require('ava');
|
const test = require('ava');
|
||||||
const store = require('./../test/fixtures/store');
|
const store = require('./../test/fixtures/store');
|
||||||
const { requirePerms } = require('./permissions');
|
const { requirePermission } = require('./permissions');
|
||||||
const supertest = require('supertest');
|
const supertest = require('supertest');
|
||||||
const getApp = require('./app');
|
const getApp = require('./app');
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ function getSetup(preRouterHook) {
|
|||||||
|
|
||||||
_app.get(
|
_app.get(
|
||||||
`${base}/protectedResource`,
|
`${base}/protectedResource`,
|
||||||
requirePerms(['READ']),
|
requirePermission('READ'),
|
||||||
(req, res) => {
|
(req, res) => {
|
||||||
res.status(200)
|
res.status(200)
|
||||||
.json({ message: 'OK' })
|
.json({ message: 'OK' })
|
||||||
|
@ -67,7 +67,7 @@ test('should revive toggle', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'name1';
|
const name = 'name1';
|
||||||
const { request, base, archiveStore, perms } = getSetup();
|
const { request, base, archiveStore, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_FEATURE);
|
perms.withPermissions(UPDATE_FEATURE);
|
||||||
archiveStore.addArchivedFeature({
|
archiveStore.addArchivedFeature({
|
||||||
name,
|
name,
|
||||||
strategies: [{ name: 'default' }],
|
strategies: [{ name: 'default' }],
|
||||||
@ -80,7 +80,7 @@ test('should create event when reviving toggle', async t => {
|
|||||||
t.plan(4);
|
t.plan(4);
|
||||||
const name = 'name1';
|
const name = 'name1';
|
||||||
const { request, base, archiveStore, eventStore, perms } = getSetup();
|
const { request, base, archiveStore, eventStore, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_FEATURE);
|
perms.withPermissions(UPDATE_FEATURE);
|
||||||
archiveStore.addArchivedFeature({
|
archiveStore.addArchivedFeature({
|
||||||
name,
|
name,
|
||||||
strategies: [{ name: 'default' }],
|
strategies: [{ name: 'default' }],
|
||||||
|
@ -79,7 +79,7 @@ test('should add version numbers for /features', t => {
|
|||||||
test('should require at least one strategy when creating a feature toggle', t => {
|
test('should require at least one strategy when creating a feature toggle', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_FEATURE);
|
perms.withPermissions(CREATE_FEATURE);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`${base}/api/admin/features`)
|
.post(`${base}/api/admin/features`)
|
||||||
@ -91,7 +91,7 @@ test('should require at least one strategy when creating a feature toggle', t =>
|
|||||||
test('should be allowed to use new toggle name', t => {
|
test('should be allowed to use new toggle name', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_FEATURE);
|
perms.withPermissions(CREATE_FEATURE);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`${base}/api/admin/features/validate`)
|
.post(`${base}/api/admin/features/validate`)
|
||||||
@ -145,7 +145,7 @@ test('should not be allowed to reuse archived toggle name', t => {
|
|||||||
test('should require at least one strategy when updating a feature toggle', t => {
|
test('should require at least one strategy when updating a feature toggle', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, featureToggleStore, base, perms } = getSetup();
|
const { request, featureToggleStore, base, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_FEATURE);
|
perms.withPermissions(UPDATE_FEATURE);
|
||||||
featureToggleStore.addFeature({
|
featureToggleStore.addFeature({
|
||||||
name: 'ts',
|
name: 'ts',
|
||||||
strategies: [{ name: 'default' }],
|
strategies: [{ name: 'default' }],
|
||||||
@ -161,7 +161,7 @@ test('should require at least one strategy when updating a feature toggle', t =>
|
|||||||
test('valid feature names should pass validation', t => {
|
test('valid feature names should pass validation', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_FEATURE);
|
perms.withPermissions(CREATE_FEATURE);
|
||||||
|
|
||||||
const validNames = [
|
const validNames = [
|
||||||
'com.example',
|
'com.example',
|
||||||
@ -190,7 +190,7 @@ test('valid feature names should pass validation', t => {
|
|||||||
test('invalid feature names should not pass validation', t => {
|
test('invalid feature names should not pass validation', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_FEATURE);
|
perms.withPermissions(CREATE_FEATURE);
|
||||||
|
|
||||||
const invalidNames = [
|
const invalidNames = [
|
||||||
'some example',
|
'some example',
|
||||||
@ -219,7 +219,7 @@ test('invalid feature names should not pass validation', t => {
|
|||||||
test('invalid feature names should have error msg', t => {
|
test('invalid feature names should have error msg', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_FEATURE);
|
perms.withPermissions(CREATE_FEATURE);
|
||||||
|
|
||||||
const name = 'ØÆ`';
|
const name = 'ØÆ`';
|
||||||
|
|
||||||
|
@ -133,7 +133,7 @@ test('should store application', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, perms } = getSetup();
|
const { request, perms } = getSetup();
|
||||||
const appName = '123!23';
|
const appName = '123!23';
|
||||||
perms.withPerms(UPDATE_APPLICATION);
|
perms.withPermissions(UPDATE_APPLICATION);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`/api/admin/metrics/applications/${appName}`)
|
.post(`/api/admin/metrics/applications/${appName}`)
|
||||||
|
@ -50,7 +50,7 @@ test('add version numbers for /stategies', t => {
|
|||||||
test('require a name when creating a new stratey', t => {
|
test('require a name when creating a new stratey', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_STRATEGY);
|
perms.withPermissions(CREATE_STRATEGY);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`${base}/api/admin/strategies`)
|
.post(`${base}/api/admin/strategies`)
|
||||||
@ -64,7 +64,7 @@ test('require a name when creating a new stratey', t => {
|
|||||||
test('require parameters array when creating a new stratey', t => {
|
test('require parameters array when creating a new stratey', t => {
|
||||||
t.plan(1);
|
t.plan(1);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_STRATEGY);
|
perms.withPermissions(CREATE_STRATEGY);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`${base}/api/admin/strategies`)
|
.post(`${base}/api/admin/strategies`)
|
||||||
@ -78,7 +78,7 @@ test('require parameters array when creating a new stratey', t => {
|
|||||||
test('create a new stratey with empty parameters', t => {
|
test('create a new stratey with empty parameters', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_STRATEGY);
|
perms.withPermissions(CREATE_STRATEGY);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.post(`${base}/api/admin/strategies`)
|
.post(`${base}/api/admin/strategies`)
|
||||||
@ -89,7 +89,7 @@ test('create a new stratey with empty parameters', t => {
|
|||||||
test('not be possible to override name', t => {
|
test('not be possible to override name', t => {
|
||||||
t.plan(0);
|
t.plan(0);
|
||||||
const { request, base, strategyStore, perms } = getSetup();
|
const { request, base, strategyStore, perms } = getSetup();
|
||||||
perms.withPerms(CREATE_STRATEGY);
|
perms.withPermissions(CREATE_STRATEGY);
|
||||||
strategyStore.addStrategy({ name: 'Testing', parameters: [] });
|
strategyStore.addStrategy({ name: 'Testing', parameters: [] });
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -102,7 +102,7 @@ test('update strategy', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'AnotherStrat';
|
const name = 'AnotherStrat';
|
||||||
const { request, base, strategyStore, perms } = getSetup();
|
const { request, base, strategyStore, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_STRATEGY);
|
perms.withPermissions(UPDATE_STRATEGY);
|
||||||
strategyStore.addStrategy({ name, parameters: [] });
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -115,7 +115,7 @@ test('not update unknown strategy', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'UnknownStrat';
|
const name = 'UnknownStrat';
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_STRATEGY);
|
perms.withPermissions(UPDATE_STRATEGY);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.put(`${base}/api/admin/strategies/${name}`)
|
.put(`${base}/api/admin/strategies/${name}`)
|
||||||
@ -127,7 +127,7 @@ test('validate format when updating strategy', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'AnotherStrat';
|
const name = 'AnotherStrat';
|
||||||
const { request, base, strategyStore, perms } = getSetup();
|
const { request, base, strategyStore, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_STRATEGY);
|
perms.withPermissions(UPDATE_STRATEGY);
|
||||||
strategyStore.addStrategy({ name, parameters: [] });
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -140,7 +140,7 @@ test('editable=false will stop delete request', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'default';
|
const name = 'default';
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(DELETE_STRATEGY);
|
perms.withPermissions(DELETE_STRATEGY);
|
||||||
|
|
||||||
return request.delete(`${base}/api/admin/strategies/${name}`).expect(500);
|
return request.delete(`${base}/api/admin/strategies/${name}`).expect(500);
|
||||||
});
|
});
|
||||||
@ -149,7 +149,7 @@ test('editable=false will stop edit request', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'default';
|
const name = 'default';
|
||||||
const { request, base, perms } = getSetup();
|
const { request, base, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_STRATEGY);
|
perms.withPermissions(UPDATE_STRATEGY);
|
||||||
|
|
||||||
return request
|
return request
|
||||||
.put(`${base}/api/admin/strategies/${name}`)
|
.put(`${base}/api/admin/strategies/${name}`)
|
||||||
@ -161,7 +161,7 @@ test('editable=true will allow delete request', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'deleteStrat';
|
const name = 'deleteStrat';
|
||||||
const { request, base, strategyStore, perms } = getSetup();
|
const { request, base, strategyStore, perms } = getSetup();
|
||||||
perms.withPerms(DELETE_STRATEGY);
|
perms.withPermissions(DELETE_STRATEGY);
|
||||||
strategyStore.addStrategy({ name, parameters: [] });
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
return request
|
return request
|
||||||
@ -174,7 +174,7 @@ test('editable=true will allow edit request', t => {
|
|||||||
t.plan(0);
|
t.plan(0);
|
||||||
const name = 'editStrat';
|
const name = 'editStrat';
|
||||||
const { request, base, strategyStore, perms } = getSetup();
|
const { request, base, strategyStore, perms } = getSetup();
|
||||||
perms.withPerms(UPDATE_STRATEGY);
|
perms.withPermissions(UPDATE_STRATEGY);
|
||||||
strategyStore.addStrategy({ name, parameters: [] });
|
strategyStore.addStrategy({ name, parameters: [] });
|
||||||
|
|
||||||
return request
|
return request
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const { Router } = require('express');
|
const { Router } = require('express');
|
||||||
const { requirePerms } = require('./../permissions');
|
const { requirePermission } = require('./../permissions');
|
||||||
/**
|
/**
|
||||||
* Base class for Controllers to standardize binding to express Router.
|
* Base class for Controllers to standardize binding to express Router.
|
||||||
*/
|
*/
|
||||||
@ -12,30 +12,46 @@ class Controller {
|
|||||||
this.extendedPerms = extendedPerms;
|
this.extendedPerms = extendedPerms;
|
||||||
}
|
}
|
||||||
|
|
||||||
get(path, handler, ...perms) {
|
get(path, handler, permission) {
|
||||||
if (this.extendedPerms && perms.length > 0) {
|
if (this.extendedPerms && permission) {
|
||||||
this.app.get(path, requirePerms(perms), handler.bind(this));
|
this.app.get(
|
||||||
|
path,
|
||||||
|
requirePermission(permission),
|
||||||
|
handler.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.app.get(path, handler.bind(this));
|
this.app.get(path, handler.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
post(path, handler, ...perms) {
|
post(path, handler, permission) {
|
||||||
if (this.extendedPerms && perms.length > 0) {
|
if (this.extendedPerms && permission) {
|
||||||
this.app.post(path, requirePerms(perms), handler.bind(this));
|
this.app.post(
|
||||||
|
path,
|
||||||
|
requirePermission(permission),
|
||||||
|
handler.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.app.post(path, handler.bind(this));
|
this.app.post(path, handler.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
put(path, handler, ...perms) {
|
put(path, handler, permission) {
|
||||||
if (this.extendedPerms && perms.length > 0) {
|
if (this.extendedPerms && permission) {
|
||||||
this.app.put(path, requirePerms(perms), handler.bind(this));
|
this.app.put(
|
||||||
|
path,
|
||||||
|
requirePermission(permission),
|
||||||
|
handler.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.app.put(path, handler.bind(this));
|
this.app.put(path, handler.bind(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(path, handler, ...perms) {
|
delete(path, handler, permission) {
|
||||||
if (this.extendedPerms && perms.length > 0) {
|
if (this.extendedPerms && permission) {
|
||||||
this.app.delete(path, requirePerms(perms), handler.bind(this));
|
this.app.delete(
|
||||||
|
path,
|
||||||
|
requirePermission(permission),
|
||||||
|
handler.bind(this)
|
||||||
|
);
|
||||||
}
|
}
|
||||||
this.app.delete(path, handler.bind(this));
|
this.app.delete(path, handler.bind(this));
|
||||||
}
|
}
|
||||||
|
4
test/fixtures/permissions.js
vendored
4
test/fixtures/permissions.js
vendored
@ -10,8 +10,8 @@ module.exports = () => {
|
|||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
withPerms(...prms) {
|
withPermissions(...perms) {
|
||||||
_perms = prms;
|
_perms = perms;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user