From a74e55daaa2bef5cc532e59f3d0cdcf66f058733 Mon Sep 17 00:00:00 2001 From: petruki <31597636+petruki@users.noreply.github.com> Date: Sun, 24 Jul 2022 18:37:16 -0700 Subject: [PATCH] Improved Slack env settings to use value pattern #335 --- src/helpers/index.js | 82 ++-------------------------- src/helpers/permission.js | 78 ++++++++++++++++++++++++++ src/services/slack.js | 5 +- tests/unit-test/helpers.test.js | 71 ++++++++++++++++++++++++ tests/unit-test/router-index.test.js | 37 ------------- 5 files changed, 157 insertions(+), 116 deletions(-) create mode 100644 src/helpers/permission.js create mode 100644 tests/unit-test/helpers.test.js delete mode 100644 tests/unit-test/router-index.test.js diff --git a/src/helpers/index.js b/src/helpers/index.js index 0035eca..b68de76 100644 --- a/src/helpers/index.js +++ b/src/helpers/index.js @@ -1,10 +1,9 @@ import { BadRequestError, PermissionError } from '../exceptions'; import { EnvType } from '../models/environment'; -import { ActionTypes, RouterTypes } from '../models/permission'; import { getDomainById } from '../services/domain'; import { getEnvironments } from '../services/environment'; import { getTeams } from '../services/team'; -import { getPermission, getPermissions } from '../services/permission'; +import { verifyPermissions, verifyPermissionsCascade } from './permission'; export async function checkEnvironmentStatusRemoval(domainId, environmentName, strategy = false) { const environment = await getEnvironments({ domain: domainId }, ['_id', 'name']); @@ -41,6 +40,10 @@ export function parseJSON(str) { } } +export function containsValue(arr, value) { + return arr?.filter(item => item.match(value)).length > 0; +} + export function formatInput(input, options = { toUpper: false, @@ -105,79 +108,4 @@ export async function verifyOwnership(admin, element, domainId, action, routerTy } return element; -} - -async function verifyPermissions(team, element, action, routerType) { - const permission = await getPermission({ - _id: { $in: team.permissions }, - action: { $in: [action, ActionTypes.ALL] }, - active: true, - router: { $in: [routerType, RouterTypes.ALL] } - }); - - if (permission) { - return verifyIdentifiers(permission, element); - } else { - throw new PermissionError(`Permission not found for this operation: '${action}' - '${routerType}'`); - } -} - -async function verifyPermissionsCascade(team, element, action, routerType) { - let orStatement = []; - if (routerType === RouterTypes.DOMAIN) { - orStatement = [ - { router: routerType }, - { router: RouterTypes.GROUP }, - { router: RouterTypes.CONFIG }, - { router: RouterTypes.STRATEGY }, - { router: RouterTypes.ALL } - ]; - } else if (routerType === RouterTypes.GROUP) { - orStatement = [ - { router: routerType }, - { router: RouterTypes.CONFIG }, - { router: RouterTypes.STRATEGY }, - { router: RouterTypes.ALL } - ]; - } else if (routerType === RouterTypes.CONFIG || routerType === RouterTypes.STRATEGY) { - orStatement = [ - { router: routerType }, - { router: RouterTypes.STRATEGY }, - { router: RouterTypes.ALL } - ]; - } - - const foundPermission = await getPermissions({ - _id: { $in: team.permissions }, - action: { $in: [action, ActionTypes.ALL] }, - active: true, - $or: orStatement - }); - - const matchedPermission = foundPermission.filter(value => value.router === routerType); - if (matchedPermission.length) { - return verifyIdentifiers(matchedPermission[0], element); - } else if (foundPermission[0]) { - return element; - } -} - -function verifyIdentifiers(permission, element) { - if (permission.identifiedBy) { - if (Array.isArray(element)) { - if (permission.values.length) { - element = element.filter(child => permission.values.includes(child[`${permission.identifiedBy}`])); - if (element.length) { - return element; - } - } - } else { - if (permission.values.includes(element[`${permission.identifiedBy}`])) { - return element; - } - } - } else { - return element; - } - throw new PermissionError('It was not possible to match the requiring element to the current permission'); } \ No newline at end of file diff --git a/src/helpers/permission.js b/src/helpers/permission.js new file mode 100644 index 0000000..250faa4 --- /dev/null +++ b/src/helpers/permission.js @@ -0,0 +1,78 @@ +import { PermissionError } from '../exceptions'; +import { ActionTypes, RouterTypes } from '../models/permission'; +import { getPermission, getPermissions } from '../services/permission'; + +export async function verifyPermissions(team, element, action, routerType) { + const permission = await getPermission({ + _id: { $in: team.permissions }, + action: { $in: [action, ActionTypes.ALL] }, + active: true, + router: { $in: [routerType, RouterTypes.ALL] } + }); + + if (permission) { + return verifyIdentifiers(permission, element); + } else { + throw new PermissionError(`Permission not found for this operation: '${action}' - '${routerType}'`); + } +} + +export async function verifyPermissionsCascade(team, element, action, routerType) { + let orStatement = []; + if (routerType === RouterTypes.DOMAIN) { + orStatement = [ + { router: routerType }, + { router: RouterTypes.GROUP }, + { router: RouterTypes.CONFIG }, + { router: RouterTypes.STRATEGY }, + { router: RouterTypes.ALL } + ]; + } else if (routerType === RouterTypes.GROUP) { + orStatement = [ + { router: routerType }, + { router: RouterTypes.CONFIG }, + { router: RouterTypes.STRATEGY }, + { router: RouterTypes.ALL } + ]; + } else if (routerType === RouterTypes.CONFIG || routerType === RouterTypes.STRATEGY) { + orStatement = [ + { router: routerType }, + { router: RouterTypes.STRATEGY }, + { router: RouterTypes.ALL } + ]; + } + + const foundPermission = await getPermissions({ + _id: { $in: team.permissions }, + action: { $in: [action, ActionTypes.ALL] }, + active: true, + $or: orStatement + }); + + const matchedPermission = foundPermission.filter(value => value.router === routerType); + if (matchedPermission.length) { + return verifyIdentifiers(matchedPermission[0], element); + } else if (foundPermission[0]) { + return element; + } +} + +function verifyIdentifiers(permission, element) { + if (permission.identifiedBy) { + if (Array.isArray(element)) { + if (permission.values.length) { + element = element.filter(child => permission.values.includes(child[`${permission.identifiedBy}`])); + if (element.length) { + return element; + } + } + } else { + if (permission.values.includes(element[`${permission.identifiedBy}`])) { + return element; + } + } + } else { + return element; + } + throw new PermissionError('It was not possible to match the requiring element to the current permission'); +} \ No newline at end of file diff --git a/src/services/slack.js b/src/services/slack.js index b6986dd..4d63835 100644 --- a/src/services/slack.js +++ b/src/services/slack.js @@ -7,6 +7,7 @@ import { getConfig } from './config'; import { getDomainById } from './domain'; import { getEnvironment } from './environment'; import { getGroupConfig } from './group-config'; +import { containsValue } from '../helpers'; /** * Validates if ticket already exists, if so, return it. @@ -160,10 +161,10 @@ export async function validateTicket(ticket_content, enterprise_id, team_id) { const ticket = await canCreateTicket(slack, ticket_content); const { ignored_environments, frozen_environments } = slack.settings; - if (frozen_environments?.includes(ticket_content.environment)) + if (containsValue(frozen_environments, ticket_content.environment)) return { result: TicketValidationType.FROZEN_ENVIRONMENT }; - if (ignored_environments?.includes(ticket_content.environment)) { + if (containsValue(ignored_environments, ticket_content.environment)) { await approveChange(slack.domain, ticket_content); return { result: TicketValidationType.IGNORED_ENVIRONMENT }; } diff --git a/tests/unit-test/helpers.test.js b/tests/unit-test/helpers.test.js new file mode 100644 index 0000000..1d50e8b --- /dev/null +++ b/tests/unit-test/helpers.test.js @@ -0,0 +1,71 @@ +const { formatInput, containsValue } = require('../../src/helpers'); + +describe('Test formatInput', () => { + + test('Should return error - Non alphanumeric input', () => { + try { + formatInput('Spaces are not allowed here'); + } catch (e) { + expect(e.message).toBe('Invalid input format. Use only alphanumeric digits.'); + } + }); + + test('Should NOT return error - Spaces allowed', () => { + try { + const input = formatInput('Spaces are not allowed here', { allowSpace: true }); + expect(input).toBe('Spaces are not allowed here'); + } catch (e) { + expect(e.message).toBe(null); + } + }); + + test('Should format input - To upper case', () => { + const input = formatInput('uppercaseme', { toUpper: true }); + expect(input).toBe('UPPERCASEME'); + }); + + test('Should format input - To lower case', () => { + const input = formatInput('UPPERCASEME', { toLower: true }); + expect(input).toBe('uppercaseme'); + }); + + test('Should format input - Replace spaces for underscore', () => { + const input = formatInput('NEW SCORE', { autoUnderscore: true }); + expect(input).toBe('NEW_SCORE'); + }); + +}); + +describe('Test containsValue', () => { + + test('Should return true when value exists in array', () => { + const result = containsValue(['value1', 'value2'], 'value1'); + expect(result).toBeTruthy(); + }); + + test('Should return false when value does not exist in array', () => { + const result = containsValue(['value1', 'value2'], 'value3'); + expect(result).toBeFalsy(); + }); + + test('Should return true when partial value exists in array', () => { + const result = containsValue(['value1', 'value2'], 'value'); + expect(result).toBeTruthy(); + }); + + test('Should return false for case sensitive values', () => { + const result = containsValue(['value1', 'value2'], 'Value1'); + expect(result).toBeFalsy(); + }); + + test('Should return false when array is empty', () => { + const result = containsValue([], 'value1'); + expect(result).toBeFalsy(); + }); + + test('Should return false when array is undefined', () => { + const result = containsValue(undefined, 'value1'); + expect(result).toBeFalsy(); + }); + +}); \ No newline at end of file diff --git a/tests/unit-test/router-index.test.js b/tests/unit-test/router-index.test.js deleted file mode 100644 index 8b570b1..0000000 --- a/tests/unit-test/router-index.test.js +++ /dev/null @@ -1,37 +0,0 @@ -const { formatInput } = require('../../src/helpers'); - -describe('Test formatInput', () => { - - test('UNIT_ROUTER_INDEX - Should return error - Non alphanumeric input', () => { - try { - formatInput('Spaces are not allowed here'); - } catch (e) { - expect(e.message).toBe('Invalid input format. Use only alphanumeric digits.'); - } - }); - - test('UNIT_ROUTER_INDEX - Should NOT return error - Spaces allowed', () => { - try { - const input = formatInput('Spaces are not allowed here', { allowSpace: true }); - expect(input).toBe('Spaces are not allowed here'); - } catch (e) { - expect(e.message).toBe(null); - } - }); - - test('UNIT_ROUTER_INDEX - Should format input - To upper case', () => { - const input = formatInput('uppercaseme', { toUpper: true }); - expect(input).toBe('UPPERCASEME'); - }); - - test('UNIT_ROUTER_INDEX - Should format input - To lower case', () => { - const input = formatInput('UPPERCASEME', { toLower: true }); - expect(input).toBe('uppercaseme'); - }); - - test('UNIT_ROUTER_INDEX - Should format input - Replace spaces for underscore', () => { - const input = formatInput('NEW SCORE', { autoUnderscore: true }); - expect(input).toBe('NEW_SCORE'); - }); - -}); \ No newline at end of file