From 920b96306ebe97e75384989b29482de0e2cfe021 Mon Sep 17 00:00:00 2001 From: Keyvan Chamani Date: Fri, 26 Jan 2024 14:03:56 -0500 Subject: [PATCH] Topics / k1ch/ Admin API / DELETE:/personas/{persona_key}/roles/{role_key} (#89) * feat: topics/k1ch/admin-delete-personas-roles / Implement API * chore: topics/k1ch/admin-delete-personas-roles/ add DB and API tests * chore: minor clean ups --- database/layer/admin-personarole.js | 17 +++++ database/test/db-admin-personaroles.test.js | 29 +++++++++ .../src/api_endpoints/personas/permissions.js | 2 +- server/src/api_endpoints/personas/roles.js | 28 +++++++- server/src/api_endpoints/personas/utils.js | 12 ++++ .../endpoint_admin_personas_roles.test.js | 65 +++++++++++++++++++ server/the-usher-openapi-spec.yaml | 28 +++++++- 7 files changed, 176 insertions(+), 5 deletions(-) diff --git a/database/layer/admin-personarole.js b/database/layer/admin-personarole.js index 7d4ef7b..4ddf274 100644 --- a/database/layer/admin-personarole.js +++ b/database/layer/admin-personarole.js @@ -126,10 +126,27 @@ const insertPersonaRoles = async (personaKey, roleKeys) => { } } +/** + * Delete a personaroles record + * + * @param {number} personaKey - The persona key + * @param {number} roleKey - The role key + * @returns {Promise} - A promise that resolves to the number of deleted records + */ +const deletePersonaRoleByKeys = async (personaKey, roleKey) => { + try { + return await usherDb('personaroles').where({ personakey: personaKey, rolekey: roleKey }).del() + } catch (err) { + console.log(err) + throw pgErrorHandler(err) + } +} + module.exports = { insertPersonaRole, deletePersonaRole, getPersonaRoles, selectPersonaRolesInTheSameTenant, insertPersonaRoles, + deletePersonaRoleByKeys, } diff --git a/database/test/db-admin-personaroles.test.js b/database/test/db-admin-personaroles.test.js index 6991b66..8d8a5ad 100644 --- a/database/test/db-admin-personaroles.test.js +++ b/database/test/db-admin-personaroles.test.js @@ -116,4 +116,33 @@ describe('Admin persona roles view', () => { await usherDb('personas').where({ key: testPersonaKey }).del() }) }) + + describe('Test Delete personas roles', () => { + let testPersonaKey + let validRoleKey + const invalidPersonaKey = 0 + + before(async () => { + const { key: roleKey } = await usherDb('roles').select('key').first() + validRoleKey = roleKey + const { key: tenantkey } = await usherDb('tenants').select('key').first() + const [persona] = await usherDb('personas').insert({ tenantkey, sub_claim: 'personarole@test' }).returning('key') + testPersonaKey = persona.key + }) + + it('Should return 0 when there is no personaroles record to delete', async () => { + const numberOfDeletedRecords = await adminPersonaRoles.deletePersonaRoleByKeys(invalidPersonaKey, validRoleKey) + assert.equal(numberOfDeletedRecords, 0) + }) + + it('Should return 1 when successfully deletes a personaroles record', async () => { + await usherDb('personaroles').insert({ personakey: testPersonaKey, rolekey: validRoleKey }) + const numberOfDeletedRecords = await adminPersonaRoles.deletePersonaRoleByKeys(testPersonaKey, validRoleKey) + assert.equal(numberOfDeletedRecords, 1) + }) + + after(async () => { + await usherDb('personas').where({ key: testPersonaKey }).del() + }) + }) }) diff --git a/server/src/api_endpoints/personas/permissions.js b/server/src/api_endpoints/personas/permissions.js index 9afce7b..70fc13e 100644 --- a/server/src/api_endpoints/personas/permissions.js +++ b/server/src/api_endpoints/personas/permissions.js @@ -16,7 +16,7 @@ const getPersonaPermissions = async (req, res, next) => { const createPersonaPermissions = async (req, res, next) => { try { const { persona_key: personaKey } = req.params - const permissionKeys = Array.from((new Set(req.body))) + const permissionKeys = [...new Set(req.body)] await Promise.all([ checkPersonaExists(personaKey), checkPersonaPermissionsValidity(personaKey, permissionKeys), diff --git a/server/src/api_endpoints/personas/roles.js b/server/src/api_endpoints/personas/roles.js index bbb8336..58d209e 100644 --- a/server/src/api_endpoints/personas/roles.js +++ b/server/src/api_endpoints/personas/roles.js @@ -1,6 +1,6 @@ const createError = require('http-errors') const dbAdminPersonaRoles = require('database/layer/admin-personarole') -const { checkPersonaExists, checkPersonaRolesValidity } = require('./utils') +const { checkPersonaExists, checkPersonaRolesValidity, checkRoleExists } = require('./utils') const getPersonaRoles = async (req, res, next) => { try { @@ -16,7 +16,7 @@ const getPersonaRoles = async (req, res, next) => { const createPersonaRoles = async (req, res, next) => { try { const { persona_key: personaKey } = req.params - const roleKeys = Array.from((new Set(req.body))) + const roleKeys = [...new Set(req.body)] await Promise.all([ checkPersonaExists(personaKey), checkPersonaRolesValidity(personaKey, roleKeys) @@ -30,7 +30,31 @@ const createPersonaRoles = async (req, res, next) => { } } +/** + * HTTP request handler + * Delete a persona role by persona key and role key and send 204 on success + * + * @param {Object} req - The request object + * @param {Object} res - The response object + * @param {Function} next - The next middleware function + * @returns {Promise} - A promise that resolves to void + */ +const deletePersonaRole = async (req, res, next) => { + try { + const { persona_key: personaKey, role_key: roleKey } = req.params + await Promise.all([ + checkPersonaExists(personaKey), + checkRoleExists(roleKey), + ]) + await dbAdminPersonaRoles.deletePersonaRoleByKeys(personaKey, roleKey) + res.status(204).send() + } catch ({ httpStatusCode = 500, message }) { + return next(createError(httpStatusCode, { message })) + } +} + module.exports = { getPersonaRoles, createPersonaRoles, + deletePersonaRole, } diff --git a/server/src/api_endpoints/personas/utils.js b/server/src/api_endpoints/personas/utils.js index a7ecb62..21b25b1 100644 --- a/server/src/api_endpoints/personas/utils.js +++ b/server/src/api_endpoints/personas/utils.js @@ -1,3 +1,4 @@ +const dbAdminRole = require('database/layer/admin-role') const dbAdminPersona = require('database/layer/admin-persona') const dbAdminPermission = require('database/layer/admin-permission') const dbAdminPersonaRoles = require('database/layer/admin-personarole') @@ -59,9 +60,20 @@ const checkPersonaPermissionsValidity = async (personaKey, permissionKeys) => { } } +const checkRoleExists = async (roleKey) => { + const role = await dbAdminRole.getRole(roleKey) + if (!role) { + throw { + httpStatusCode: 404, + message: 'Role does not exist!' + } + } +} + module.exports = { checkPersonaExists, checkPermissionExists, checkPersonaRolesValidity, checkPersonaPermissionsValidity, + checkRoleExists, } diff --git a/server/test/endpoint_admin_personas_roles.test.js b/server/test/endpoint_admin_personas_roles.test.js index d513066..e3a25e4 100644 --- a/server/test/endpoint_admin_personas_roles.test.js +++ b/server/test/endpoint_admin_personas_roles.test.js @@ -151,6 +151,71 @@ describe('Admin Personas Roles', () => { }) }) + describe('DELETE:/personas/{persona_key}/roles/{role_key}', () => { + /** + * Helper function to make an HTTPS request to delete a persona role. + * + * @param {string} roleKey - The role key + * @param {string} personaKey - The persona key + * @param {Object} header - The request headers + * @returns {Promise} - A promise that resolves to the HTTP response + */ + const deletePersonasRoles = async (roleKey, personaKey = testPersonaKey, header = requestHeaders) => { + return await fetch(`${url}/personas/${personaKey}/roles/${roleKey}`, { + method: 'DELETE', + headers: header, + }) + } + + it('should return 204, successful attempt to delete a persona role', async () => { + const response = await deletePersonasRoles(validRoleKey) + assert.equal(response.status, 204) + }) + + it('should return 204, delete a persona role successfully', async () => { + const [newPersonaRole] = await usherDb('personaroles') + .insert({ personakey: testPersonaKey, rolekey: validRoleKey }).returning('*') + assert.equal(newPersonaRole.personakey, testPersonaKey) + const response = await deletePersonasRoles(newPersonaRole.rolekey) + assert.equal(response.status, 204) + const personaRole = await usherDb('personaroles').select('*').where({ personakey: testPersonaKey, rolekey: validRoleKey }) + assert.equal(personaRole.length, 0) + }) + + it('should return 400, two different invalid requests', async () => { + const [invalidRoleKeyResponse, invalidPersonaKeyResponse] = await Promise.all( + [ + deletePersonasRoles('a'), + deletePersonasRoles(validRoleKey, 'a'), + ] + ) + assert.equal([ + invalidRoleKeyResponse.status, + invalidPersonaKeyResponse.status].every((status) => status === 400), true) + }) + + it('should return 401, unauthorized token', async () => { + const userAccessToken = await getTestUser1IdPToken() + const response = await deletePersonasRoles( + validRoleKey, + testPersonaKey, + { + ...requestHeaders, + Authorization: `Bearer ${userAccessToken}` + }) + assert.equal(response.status, 401) + }) + + it('should return 404, fail to delete persona roles for an invalid persona', async () => { + const response = await deletePersonasRoles(validRoleKey, invalidPersona) + assert.equal(response.status, 404) + }) + + afterEach(async () => { + await usherDb('personaroles').where({ personakey: testPersonaKey }).del() + }) + }) + after(async () => { await usherDb('personas').where({ key: testPersonaKey }).del() }) diff --git a/server/the-usher-openapi-spec.yaml b/server/the-usher-openapi-spec.yaml index 5fbc565..b9c6f3d 100644 --- a/server/the-usher-openapi-spec.yaml +++ b/server/the-usher-openapi-spec.yaml @@ -570,10 +570,10 @@ paths: responses: 204: description: Empty response on success - 401: - $ref: '#/components/responses/Unauthorized' 400: $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' 404: $ref: '#/components/responses/NotFound' 500: @@ -642,6 +642,30 @@ paths: 500: $ref: '#/components/responses/InternalError' + /personas/{persona_key}/roles/{role_key}: + delete: + 'x-swagger-router-controller': 'personas/roles' + operationId: deletePersonaRole + summary: Removes a role for subject persona + parameters: + - $ref: '#/components/parameters/personaKeyPathParam' + - $ref: '#/components/parameters/roleKeyPathParam' + tags: + - Admin APIs + security: + - bearerAdminAuth: [] + responses: + 204: + description: Empty response on success + 400: + $ref: '#/components/responses/BadRequest' + 401: + $ref: '#/components/responses/Unauthorized' + 404: + $ref: '#/components/responses/NotFound' + 500: + $ref: '#/components/responses/InternalError' + /clients: post: 'x-swagger-router-controller': 'clients/index'