From 136deedfa38174a145dde7195d7d41aa709ccbdc Mon Sep 17 00:00:00 2001 From: Kyle Morel Date: Mon, 22 Jul 2024 12:09:01 -0700 Subject: [PATCH] Only get roles for current initiative --- app/src/middleware/authorization.ts | 6 +++-- app/src/middleware/initiative.ts | 34 +++++++++++++++++++++++++++++ app/src/routes/v1/index.ts | 3 +++ app/src/services/index.ts | 1 + app/src/services/initiative.ts | 22 +++++++++++++++++++ app/src/services/yars.ts | 9 +++++++- app/src/types/CurrentUser.ts | 5 +++-- 7 files changed, 75 insertions(+), 5 deletions(-) create mode 100644 app/src/middleware/initiative.ts create mode 100644 app/src/services/initiative.ts diff --git a/app/src/middleware/authorization.ts b/app/src/middleware/authorization.ts index e7507a3d..25e622bc 100644 --- a/app/src/middleware/authorization.ts +++ b/app/src/middleware/authorization.ts @@ -4,7 +4,7 @@ import Problem from 'api-problem'; import { userService, yarsService } from '../services'; import type { NextFunction, Request, Response } from '../interfaces/IExpress'; -import { Scope } from '../utils/enums/application'; +import { Initiative, Scope } from '../utils/enums/application'; import { getCurrentIdentity } from '../utils/utils'; import { NIL } from 'uuid'; @@ -33,7 +33,9 @@ export const hasPermission = (resource: string, action: string) => { const roles = await yarsService.getIdentityRoles((req.currentUser?.tokenPayload as any).preferred_username); const permissions = await Promise.all( - roles.map((x) => yarsService.getRolePermissionDetails(x.roleId, resource, action)) + roles.map((x) => + yarsService.getRolePermissionDetails(x.roleId, req.currentUser?.initiative as Initiative, resource, action) + ) ).then((x) => x.flat()); if (!permissions || permissions.length === 0) { diff --git a/app/src/middleware/initiative.ts b/app/src/middleware/initiative.ts new file mode 100644 index 00000000..2b472bd0 --- /dev/null +++ b/app/src/middleware/initiative.ts @@ -0,0 +1,34 @@ +// @ts-expect-error api-problem lacks a defined interface; code still works fine +import Problem from 'api-problem'; + +import { Initiative } from '../utils/enums/application'; + +import type { NextFunction, Request, Response } from '../interfaces/IExpress'; + +/** + * @function setInitiative + * Injects the given initiative into the currentUser + * @param {string} initiative An initiative code + * @returns {function} Express middleware function + * @throws The error encountered upon failure + */ +export const setInitiative = (initiative: Initiative) => { + return async (req: Request, res: Response, next: NextFunction) => { + try { + if (req.currentUser) { + req.currentUser.initiative = initiative; + } else { + throw new Problem(403, { + detail: 'Unable to determine initiative', + instance: req.originalUrl + }); + } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + } catch (err: any) { + return next(new Problem(403, { detail: err.message, instance: req.originalUrl })); + } + + // Continue middleware + next(); + }; +}; diff --git a/app/src/routes/v1/index.ts b/app/src/routes/v1/index.ts index 95bca13a..697dee97 100644 --- a/app/src/routes/v1/index.ts +++ b/app/src/routes/v1/index.ts @@ -1,6 +1,7 @@ import express from 'express'; import { currentUser } from '../../middleware/authentication'; +import { setInitiative } from '../../middleware/initiative'; import document from './document'; import enquiry from './enquiry'; @@ -10,9 +11,11 @@ import roadmap from './roadmap'; import sso from './sso'; import submission from './submission'; import user from './user'; +import { Initiative } from '../../utils/enums/application'; const router = express.Router(); router.use(currentUser); +router.use(setInitiative(Initiative.HOUSING)); // Base v1 Responder router.get('/', (_req, res) => { diff --git a/app/src/services/index.ts b/app/src/services/index.ts index d5c30876..8b00248a 100644 --- a/app/src/services/index.ts +++ b/app/src/services/index.ts @@ -3,6 +3,7 @@ export { default as comsService } from './coms'; export { default as documentService } from './document'; export { default as emailService } from './email'; export { default as enquiryService } from './enquiry'; +export { default as initiativeService } from './initiative'; export { default as noteService } from './note'; export { default as permitService } from './permit'; export { default as ssoService } from './sso'; diff --git a/app/src/services/initiative.ts b/app/src/services/initiative.ts new file mode 100644 index 00000000..15215774 --- /dev/null +++ b/app/src/services/initiative.ts @@ -0,0 +1,22 @@ +import prisma from '../db/dataConnection'; + +import { Initiative } from '../utils/enums/application'; + +const service = { + /** + * @function getInitiative + * Create an activity for the given initiative with a unique identifier + * @param {string} initiative The initiative ID + * @returns {Promise} The result of running the findFirst operation + */ + getInitiative: async (initiative: Initiative) => { + const result = await prisma.initiative.findFirstOrThrow({ + where: { + code: initiative + } + }); + return { initiativeId: result.initiative_id, code: result.code, label: result.label }; + } +}; + +export default service; diff --git a/app/src/services/yars.ts b/app/src/services/yars.ts index ed8a5293..ba9414df 100644 --- a/app/src/services/yars.ts +++ b/app/src/services/yars.ts @@ -1,6 +1,7 @@ /* eslint-disable no-useless-catch */ import prisma from '../db/dataConnection'; +import { Initiative } from '../utils/enums/application'; const service = { /** @@ -23,11 +24,17 @@ const service = { } }, - getRolePermissionDetails: async (roleId: number, resourceName: string, actionName: string) => { + getRolePermissionDetails: async ( + roleId: number, + initiativeName: Initiative, + resourceName: string, + actionName: string + ) => { try { const result = await prisma.role_permission_vw.findMany({ where: { role_id: roleId, + initiative_name: initiativeName.toLowerCase(), resource_name: resourceName, action_name: actionName } diff --git a/app/src/types/CurrentUser.ts b/app/src/types/CurrentUser.ts index d64371ac..92b8b3c9 100644 --- a/app/src/types/CurrentUser.ts +++ b/app/src/types/CurrentUser.ts @@ -1,10 +1,11 @@ import jwt from 'jsonwebtoken'; import { ApiScope } from './ApiScope'; -import { AuthType } from '../utils/enums/application'; +import { AuthType, Initiative } from '../utils/enums/application'; export type CurrentUser = { authType: AuthType; - apiScope: ApiScope; + apiScope?: ApiScope; + initiative?: Initiative; tokenPayload: string | jwt.JwtPayload | null; };