diff --git a/src/api-docs/schemas/admin.js b/src/api-docs/schemas/admin.js index 82ed383..1d9e4ad 100644 --- a/src/api-docs/schemas/admin.js +++ b/src/api-docs/schemas/admin.js @@ -127,6 +127,9 @@ export default { type: 'string' } } + }, + environment: { + type: 'string' } } }, diff --git a/src/helpers/index.js b/src/helpers/index.js index 11d3a43..6a02f18 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -112,7 +112,7 @@ export function validatePagingArgs(args) { return true; } -export async function verifyOwnership(admin, element, domainId, action, routerType, cascade = false) { +export async function verifyOwnership(admin, element, domainId, action, routerType, cascade = false, environment = undefined) { const domain = await getDomainById(domainId); if (admin._id.equals(domain.owner)) { return element; @@ -121,15 +121,15 @@ export async function verifyOwnership(admin, element, domainId, action, routerTy const teams = await getTeams({ _id: { $in: admin.teams }, domain: domain._id, active: true }); if (!teams.length || !admin.teams.length) { throw new PermissionError('It was not possible to find any team that allows you to proceed with this operation'); - } + } let hasPermission = []; let allowedElement; for (const team of teams) { if (cascade) { - allowedElement = await verifyPermissionsCascade(team, element, action, routerType); + allowedElement = await verifyPermissionsCascade(team, element, action, routerType, environment); } else { - allowedElement = await verifyPermissions(team, element, action, routerType); + allowedElement = await verifyPermissions(team, element, action, routerType, environment); } if (allowedElement) { diff --git a/src/helpers/permission.js b/src/helpers/permission.js index 4034681..9128717 100644 --- a/src/helpers/permission.js +++ b/src/helpers/permission.js @@ -1,7 +1,7 @@ import { ActionTypes, RouterTypes } from '../models/permission'; import { getPermission, getPermissions } from '../services/permission'; -export async function verifyPermissions(team, element, action, routerType) { +export async function verifyPermissions(team, element, action, routerType, environment) { const permission = await getPermission({ _id: { $in: team.permissions }, action: { $in: [action, ActionTypes.ALL] }, @@ -9,14 +9,14 @@ export async function verifyPermissions(team, element, action, routerType) { router: { $in: [routerType, RouterTypes.ALL] } }); - if (!permission) { + if (!permission || !verifyEnvironment(permission, environment)) { return undefined; } return verifyIdentifiers(permission, element); } -export async function verifyPermissionsCascade(team, element, action, routerType) { +export async function verifyPermissionsCascade(team, element, action, routerType, environment) { let orStatement = []; if (routerType === RouterTypes.DOMAIN) { orStatement = [ @@ -49,9 +49,9 @@ export async function verifyPermissionsCascade(team, element, action, routerType }); const matchedPermission = foundPermission.filter(value => value.router === routerType); - if (matchedPermission.length) { + if (matchedPermission.length && verifyEnvironment(matchedPermission[0], environment)) { return verifyIdentifiers(matchedPermission[0], element); - } else if (foundPermission[0]) { + } else if (foundPermission[0] && verifyEnvironment(foundPermission[0], environment)) { return element; } } @@ -73,4 +73,12 @@ function verifyIdentifiers(permission, element) { } return element; +} + +function verifyEnvironment(permission, environment) { + if (permission.environments?.length) { + return environment && permission.environments.includes(environment); + } + + return true; } \ No newline at end of file diff --git a/src/routers/admin.js b/src/routers/admin.js index 6f8a932..c07aa94 100644 --- a/src/routers/admin.js +++ b/src/routers/admin.js @@ -100,7 +100,8 @@ router.get('/admin/me', auth, async (req, res) => { router.post('/admin/collaboration/permission', auth, [ check('domain', 'Domain Id is required').isMongoId(), check('action', 'Array of actions is required').isArray({ min: 1 }), - check('router', 'Router name is required').isLength({ min: 1 }) + check('router', 'Router name is required').isLength({ min: 1 }), + check('environment').isString().optional() ], validate, async (req, res) => { const element = { _id: req.body.element?.id, @@ -112,7 +113,8 @@ router.post('/admin/collaboration/permission', auth, [ let result = []; for (const action_perm of req.body.action) { try { - await verifyOwnership(req.admin, element, req.body.domain, action_perm, req.body.router); + await verifyOwnership(req.admin, element, req.body.domain, action_perm, + req.body.router, false, req.body.environment); result.push({ action: action_perm.toString(), result: 'ok' }); } catch (e) { result.push({ action : action_perm.toString(), result: 'nok' }); diff --git a/tests/admin.test.js b/tests/admin.test.js index 50b8649..bc48074 100644 --- a/tests/admin.test.js +++ b/tests/admin.test.js @@ -18,6 +18,7 @@ import { import { Team } from '../src/models/team'; import swaggerDocument from '../src/api-docs/swagger-document'; import { RouterTypes } from '../src/models/permission'; +import { EnvType } from '../src/models/environment'; afterAll(async () => { await new Promise(resolve => setTimeout(resolve, 1000)); @@ -954,7 +955,8 @@ describe('Testing Admin collaboration endpoint - Reading permissions', () => { .send({ domain: domainId, action: ['READ', 'UPDATE', 'CREATE'], - router: RouterTypes.GROUP + router: RouterTypes.GROUP, + environment: EnvType.DEFAULT }) .expect(200); @@ -967,6 +969,24 @@ describe('Testing Admin collaboration endpoint - Reading permissions', () => { const create = response.body.filter(permission => permission.action === 'CREATE'); expect(create[0].result).toEqual('nok'); }); + + test('ADMIN_SUITE - Should read permissions given request - Access forbidden for staging', async () => { + const response = await request(app) + .post('/admin/collaboration/permission') + .set('Authorization', `Bearer ${token}`) + .send({ + domain: domainId, + action: ['READ'], + router: RouterTypes.GROUP, + environment: 'staging' + }) + .expect(200); + + expect(response.body.length > 0).toEqual(true); + + const read = response.body.filter(permission => permission.action === 'READ'); + expect(read[0].result).toEqual('nok'); + }); }); diff --git a/tests/fixtures/db_api.js b/tests/fixtures/db_api.js index ce0d9ea..5dbfc5e 100644 --- a/tests/fixtures/db_api.js +++ b/tests/fixtures/db_api.js @@ -72,7 +72,8 @@ export const permission1 = { _id: permission1Id, action: ActionTypes.READ, active: true, - router: RouterTypes.GROUP + router: RouterTypes.GROUP, + environments: [EnvType.DEFAULT, 'dev'] }; export const component1Id = new mongoose.Types.ObjectId(); diff --git a/tests/fixtures/db_team_permission.js b/tests/fixtures/db_team_permission.js index 3a82cbc..97645ac 100644 --- a/tests/fixtures/db_team_permission.js +++ b/tests/fixtures/db_team_permission.js @@ -66,6 +66,15 @@ export const permission1 = { router: RouterTypes.CONFIG }; +export const permission11Id = new mongoose.Types.ObjectId(); +export const permission11 = { + _id: permission11Id, + action: ActionTypes.UPDATE, + active: true, + router: RouterTypes.CONFIG, + environments: ['dev'] +}; + export const permission2Id = new mongoose.Types.ObjectId(); export const permission2 = { _id: permission2Id, @@ -118,7 +127,7 @@ export const team1 = { domain: domainId, name: 'Team 1', active: true, - permissions: [permission1Id, permission2Id, permission3Id] + permissions: [permission1Id, permission11Id, permission2Id, permission3Id] }; export const team2Id = new mongoose.Types.ObjectId(); @@ -188,6 +197,7 @@ export const setupDatabase = async () => { await new GroupConfig(groupConfig2Document).save(); await new Config(configDocument).save(); await new Permission(permission1).save(); + await new Permission(permission11).save(); await new Permission(permission2).save(); await new Permission(permission21).save(); await new Permission(permission22).save(); diff --git a/tests/unit-test/verify-ownership.test.js b/tests/unit-test/verify-ownership.test.js index e0db7c4..a7061ff 100644 --- a/tests/unit-test/verify-ownership.test.js +++ b/tests/unit-test/verify-ownership.test.js @@ -9,22 +9,33 @@ import { setupDatabase, adminMasterAccount, adminAccount, + adminAccount2, + adminAccount3, domainDocument, groupConfig2Document, configDocument, domainId, + team1Id, permission1Id, + permission11Id, permission2Id, - permission3Id, - adminAccount2, - team1Id, - adminAccount3, permission21Id, - permission4Id, - permission22Id + permission22Id, + permission3Id, + permission4Id } from '../fixtures/db_team_permission'; import { PermissionError } from '../../src/exceptions'; +const disableAllPermissions = async () => { + await changePermissionStatus(permission1Id, false); + await changePermissionStatus(permission11Id, false); + await changePermissionStatus(permission2Id, false); + await changePermissionStatus(permission21Id, false); + await changePermissionStatus(permission22Id, false); + await changePermissionStatus(permission3Id, false); + await changePermissionStatus(permission4Id, false); +}; + const changePermissionStatus = async (permissionId, status) => { const permission = await Permission.findById(permissionId).exec(); permission.active = status; @@ -50,15 +61,7 @@ afterAll(async () => { describe('Success tests', () => { beforeAll(setupDatabase); - - beforeEach(async () => { - await changePermissionStatus(permission1Id, false); - await changePermissionStatus(permission2Id, false); - await changePermissionStatus(permission21Id, false); - await changePermissionStatus(permission22Id, false); - await changePermissionStatus(permission3Id, false); - await changePermissionStatus(permission4Id, false); - }); + beforeEach(disableAllPermissions); test('UNIT_TEAM_PERMISSION_SUITE - Should allow access - Element owner', async () => { try { @@ -69,7 +72,7 @@ describe('Success tests', () => { ActionTypes.READ, RouterTypes.DOMAIN); - expect(element._id).toEqual(domainDocument._id); + expect(element).toMatchObject(domainDocument); } catch (e) { expect(e).toBeNull(); } @@ -89,7 +92,51 @@ describe('Success tests', () => { ActionTypes.READ, RouterTypes.GROUP); - expect(element._id).toEqual(groupConfig2Document._id); + expect(element).toMatchObject(groupConfig2Document); + } catch (e) { + expect(e).toBeNull(); + } + }); + + test('UNIT_TEAM_PERMISSION_SUITE - Should allow access - Member has permission to environment', async () => { + //given + //enabled Update - Switcher (update only in dev environment) + await changePermissionStatus(permission11Id, true); + + //test + try { + const element = await verifyOwnership( + adminAccount, + configDocument, + domainDocument, + ActionTypes.UPDATE, + RouterTypes.CONFIG, + false, + 'dev'); + + expect(element).toMatchObject(configDocument); + } catch (e) { + expect(e).toBeNull(); + } + }); + + test('UNIT_TEAM_PERMISSION_SUITE - Should allow access - Member has permission to environment - Cascade', async () => { + //given + //enabled Update - Switcher (update only in dev environment) + await changePermissionStatus(permission11Id, true); + + //test + try { + const element = await verifyOwnership( + adminAccount, + configDocument, + domainDocument, + ActionTypes.UPDATE, + RouterTypes.CONFIG, + true, + 'dev'); + + expect(element).toMatchObject(configDocument); } catch (e) { expect(e).toBeNull(); } @@ -113,7 +160,7 @@ describe('Success tests', () => { RouterTypes.GROUP, true); - expect(element._id).toEqual(groupConfig2Document._id); + expect(element).toMatchObject(groupConfig2Document); } catch (e) { expect(e).toBeNull(); } @@ -254,15 +301,7 @@ describe('Success tests', () => { describe('Error tests', () => { beforeAll(setupDatabase); - - beforeEach(async () => { - await changePermissionStatus(permission1Id, false); - await changePermissionStatus(permission2Id, false); - await changePermissionStatus(permission21Id, false); - await changePermissionStatus(permission22Id, false); - await changePermissionStatus(permission3Id, false); - await changePermissionStatus(permission4Id, false); - }); + beforeEach(disableAllPermissions); test('UNIT_TEAM_PERMISSION_SUITE - Should NOT allow access - Permission innactive', async () => { await changePermissionStatus(permission2Id, false); @@ -344,4 +383,21 @@ describe('Error tests', () => { }).rejects.toThrow(new PermissionError(`Action forbidden`)); }); + test('UNIT_TEAM_PERMISSION_SUITE - Should NOT allow access - Member does not have permission to environment', async () => { + //given + //enabled Update - Switcher (update only in dev environment) + await changePermissionStatus(permission11Id, true); + + expect(async () => { + await verifyOwnership( + adminAccount, + configDocument, + domainDocument, + ActionTypes.UPDATE, + RouterTypes.CONFIG, + false, + 'default'); + }).rejects.toThrow(new PermissionError(`Action forbidden`)); + }); + }); \ No newline at end of file