mirror of
				https://github.com/Unleash/unleash.git
				synced 2025-10-27 11:02:16 +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 || '', | ||||
|     serverMetrics: true, | ||||
|     enableLegacyRoutes: true, | ||||
|     extendedPermissions: false, | ||||
|     publicFolder, | ||||
|     enableRequestLogger: isDev(), | ||||
|     secret: 'UNLEASH-SECRET', | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| const MissingPermission = require('./missing-permission'); | ||||
| 
 | ||||
| const ADMIN = 'ADMIN'; | ||||
| const CREATE_FEATURE = 'CREATE_FEATURE'; | ||||
| const UPDATE_FEATURE = 'UPDATE_FEATURE'; | ||||
| @ -9,29 +11,30 @@ const UPDATE_STRATEGY = 'UPDATE_STRATEGY'; | ||||
| const DELETE_STRATEGY = 'DELETE_STRATEGY'; | ||||
| const UPDATE_APPLICATION = 'UPDATE_APPLICATION'; | ||||
| 
 | ||||
| function requirePerms(prms) { | ||||
| function requirePermission(permission) { | ||||
|     return (req, res, next) => { | ||||
|         for (const permission of prms) { | ||||
|             if ( | ||||
|                 req.user && | ||||
|                 req.user.permissions && | ||||
|                 (req.user.permissions.indexOf(ADMIN) !== -1 || | ||||
|                     req.user.permissions.indexOf(permission) !== -1) | ||||
|             ) { | ||||
|                 return next(); | ||||
|             } | ||||
|         if ( | ||||
|             req.user && | ||||
|             req.user.permissions && | ||||
|             (req.user.permissions.indexOf(ADMIN) !== -1 || | ||||
|                 req.user.permissions.indexOf(permission) !== -1) | ||||
|         ) { | ||||
|             return next(); | ||||
|         } | ||||
|         return res | ||||
|             .status(403) | ||||
|             .json({ | ||||
|                 message: 'Missing permissions to perform this action.', | ||||
|             }) | ||||
|             .json( | ||||
|                 new MissingPermission({ | ||||
|                     permission, | ||||
|                     message: `You require ${permission} to perform this action`, | ||||
|                 }) | ||||
|             ) | ||||
|             .end(); | ||||
|     }; | ||||
| } | ||||
| 
 | ||||
| module.exports = { | ||||
|     requirePerms, | ||||
|     requirePermission, | ||||
|     ADMIN, | ||||
|     CREATE_FEATURE, | ||||
|     UPDATE_FEATURE, | ||||
|  | ||||
| @ -2,7 +2,7 @@ | ||||
| 
 | ||||
| const test = require('ava'); | ||||
| const store = require('./../test/fixtures/store'); | ||||
| const { requirePerms } = require('./permissions'); | ||||
| const { requirePermission } = require('./permissions'); | ||||
| const supertest = require('supertest'); | ||||
| const getApp = require('./app'); | ||||
| 
 | ||||
| @ -22,7 +22,7 @@ function getSetup(preRouterHook) { | ||||
| 
 | ||||
|             _app.get( | ||||
|                 `${base}/protectedResource`, | ||||
|                 requirePerms(['READ']), | ||||
|                 requirePermission('READ'), | ||||
|                 (req, res) => { | ||||
|                     res.status(200) | ||||
|                         .json({ message: 'OK' }) | ||||
|  | ||||
| @ -67,7 +67,7 @@ test('should revive toggle', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'name1'; | ||||
|     const { request, base, archiveStore, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_FEATURE); | ||||
|     perms.withPermissions(UPDATE_FEATURE); | ||||
|     archiveStore.addArchivedFeature({ | ||||
|         name, | ||||
|         strategies: [{ name: 'default' }], | ||||
| @ -80,7 +80,7 @@ test('should create event when reviving toggle', async t => { | ||||
|     t.plan(4); | ||||
|     const name = 'name1'; | ||||
|     const { request, base, archiveStore, eventStore, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_FEATURE); | ||||
|     perms.withPermissions(UPDATE_FEATURE); | ||||
|     archiveStore.addArchivedFeature({ | ||||
|         name, | ||||
|         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 => { | ||||
|     t.plan(0); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_FEATURE); | ||||
|     perms.withPermissions(CREATE_FEATURE); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(0); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_FEATURE); | ||||
|     perms.withPermissions(CREATE_FEATURE); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(0); | ||||
|     const { request, featureToggleStore, base, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_FEATURE); | ||||
|     perms.withPermissions(UPDATE_FEATURE); | ||||
|     featureToggleStore.addFeature({ | ||||
|         name: 'ts', | ||||
|         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 => { | ||||
|     t.plan(0); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_FEATURE); | ||||
|     perms.withPermissions(CREATE_FEATURE); | ||||
| 
 | ||||
|     const validNames = [ | ||||
|         'com.example', | ||||
| @ -190,7 +190,7 @@ test('valid feature names should pass validation', t => { | ||||
| test('invalid feature names should not pass validation', t => { | ||||
|     t.plan(0); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_FEATURE); | ||||
|     perms.withPermissions(CREATE_FEATURE); | ||||
| 
 | ||||
|     const invalidNames = [ | ||||
|         'some example', | ||||
| @ -219,7 +219,7 @@ test('invalid feature names should not pass validation', t => { | ||||
| test('invalid feature names should have error msg', t => { | ||||
|     t.plan(1); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_FEATURE); | ||||
|     perms.withPermissions(CREATE_FEATURE); | ||||
| 
 | ||||
|     const name = 'ØÆ`'; | ||||
| 
 | ||||
|  | ||||
| @ -133,7 +133,7 @@ test('should store application', t => { | ||||
|     t.plan(0); | ||||
|     const { request, perms } = getSetup(); | ||||
|     const appName = '123!23'; | ||||
|     perms.withPerms(UPDATE_APPLICATION); | ||||
|     perms.withPermissions(UPDATE_APPLICATION); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(1); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_STRATEGY); | ||||
|     perms.withPermissions(CREATE_STRATEGY); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(1); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_STRATEGY); | ||||
|     perms.withPermissions(CREATE_STRATEGY); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(0); | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_STRATEGY); | ||||
|     perms.withPermissions(CREATE_STRATEGY); | ||||
| 
 | ||||
|     return request | ||||
|         .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 => { | ||||
|     t.plan(0); | ||||
|     const { request, base, strategyStore, perms } = getSetup(); | ||||
|     perms.withPerms(CREATE_STRATEGY); | ||||
|     perms.withPermissions(CREATE_STRATEGY); | ||||
|     strategyStore.addStrategy({ name: 'Testing', parameters: [] }); | ||||
| 
 | ||||
|     return request | ||||
| @ -102,7 +102,7 @@ test('update strategy', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'AnotherStrat'; | ||||
|     const { request, base, strategyStore, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_STRATEGY); | ||||
|     perms.withPermissions(UPDATE_STRATEGY); | ||||
|     strategyStore.addStrategy({ name, parameters: [] }); | ||||
| 
 | ||||
|     return request | ||||
| @ -115,7 +115,7 @@ test('not update unknown strategy', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'UnknownStrat'; | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_STRATEGY); | ||||
|     perms.withPermissions(UPDATE_STRATEGY); | ||||
| 
 | ||||
|     return request | ||||
|         .put(`${base}/api/admin/strategies/${name}`) | ||||
| @ -127,7 +127,7 @@ test('validate format when updating strategy', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'AnotherStrat'; | ||||
|     const { request, base, strategyStore, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_STRATEGY); | ||||
|     perms.withPermissions(UPDATE_STRATEGY); | ||||
|     strategyStore.addStrategy({ name, parameters: [] }); | ||||
| 
 | ||||
|     return request | ||||
| @ -140,7 +140,7 @@ test('editable=false will stop delete request', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'default'; | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(DELETE_STRATEGY); | ||||
|     perms.withPermissions(DELETE_STRATEGY); | ||||
| 
 | ||||
|     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); | ||||
|     const name = 'default'; | ||||
|     const { request, base, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_STRATEGY); | ||||
|     perms.withPermissions(UPDATE_STRATEGY); | ||||
| 
 | ||||
|     return request | ||||
|         .put(`${base}/api/admin/strategies/${name}`) | ||||
| @ -161,7 +161,7 @@ test('editable=true will allow delete request', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'deleteStrat'; | ||||
|     const { request, base, strategyStore, perms } = getSetup(); | ||||
|     perms.withPerms(DELETE_STRATEGY); | ||||
|     perms.withPermissions(DELETE_STRATEGY); | ||||
|     strategyStore.addStrategy({ name, parameters: [] }); | ||||
| 
 | ||||
|     return request | ||||
| @ -174,7 +174,7 @@ test('editable=true will allow edit request', t => { | ||||
|     t.plan(0); | ||||
|     const name = 'editStrat'; | ||||
|     const { request, base, strategyStore, perms } = getSetup(); | ||||
|     perms.withPerms(UPDATE_STRATEGY); | ||||
|     perms.withPermissions(UPDATE_STRATEGY); | ||||
|     strategyStore.addStrategy({ name, parameters: [] }); | ||||
| 
 | ||||
|     return request | ||||
|  | ||||
| @ -1,7 +1,7 @@ | ||||
| 'use strict'; | ||||
| 
 | ||||
| const { Router } = require('express'); | ||||
| const { requirePerms } = require('./../permissions'); | ||||
| const { requirePermission } = require('./../permissions'); | ||||
| /** | ||||
|  * Base class for Controllers to standardize binding to express Router. | ||||
|  */ | ||||
| @ -12,30 +12,46 @@ class Controller { | ||||
|         this.extendedPerms = extendedPerms; | ||||
|     } | ||||
| 
 | ||||
|     get(path, handler, ...perms) { | ||||
|         if (this.extendedPerms && perms.length > 0) { | ||||
|             this.app.get(path, requirePerms(perms), handler.bind(this)); | ||||
|     get(path, handler, permission) { | ||||
|         if (this.extendedPerms && permission) { | ||||
|             this.app.get( | ||||
|                 path, | ||||
|                 requirePermission(permission), | ||||
|                 handler.bind(this) | ||||
|             ); | ||||
|         } | ||||
|         this.app.get(path, handler.bind(this)); | ||||
|     } | ||||
| 
 | ||||
|     post(path, handler, ...perms) { | ||||
|         if (this.extendedPerms && perms.length > 0) { | ||||
|             this.app.post(path, requirePerms(perms), handler.bind(this)); | ||||
|     post(path, handler, permission) { | ||||
|         if (this.extendedPerms && permission) { | ||||
|             this.app.post( | ||||
|                 path, | ||||
|                 requirePermission(permission), | ||||
|                 handler.bind(this) | ||||
|             ); | ||||
|         } | ||||
|         this.app.post(path, handler.bind(this)); | ||||
|     } | ||||
| 
 | ||||
|     put(path, handler, ...perms) { | ||||
|         if (this.extendedPerms && perms.length > 0) { | ||||
|             this.app.put(path, requirePerms(perms), handler.bind(this)); | ||||
|     put(path, handler, permission) { | ||||
|         if (this.extendedPerms && permission) { | ||||
|             this.app.put( | ||||
|                 path, | ||||
|                 requirePermission(permission), | ||||
|                 handler.bind(this) | ||||
|             ); | ||||
|         } | ||||
|         this.app.put(path, handler.bind(this)); | ||||
|     } | ||||
| 
 | ||||
|     delete(path, handler, ...perms) { | ||||
|         if (this.extendedPerms && perms.length > 0) { | ||||
|             this.app.delete(path, requirePerms(perms), handler.bind(this)); | ||||
|     delete(path, handler, permission) { | ||||
|         if (this.extendedPerms && permission) { | ||||
|             this.app.delete( | ||||
|                 path, | ||||
|                 requirePermission(permission), | ||||
|                 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(); | ||||
|             }); | ||||
|         }, | ||||
|         withPerms(...prms) { | ||||
|             _perms = prms; | ||||
|         withPermissions(...perms) { | ||||
|             _perms = perms; | ||||
|         }, | ||||
|     }; | ||||
| }; | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user