diff --git a/api/src/__mocks__/db.ts b/api/src/__mocks__/db.ts index 771b31b503..8b528995cc 100644 --- a/api/src/__mocks__/db.ts +++ b/api/src/__mocks__/db.ts @@ -65,11 +65,6 @@ export class MockReq { query = {}; params = {}; body = {}; - files: any[] = []; - // Exists on authenticated requests. @see authentication.ts and authorization.ts - keycloak_token?: Record; - // Exists on authenticated requests. @see authentication.ts and authorization.ts - system_user?: Record; } export type ExtendedMockRes = MockRes & Response; diff --git a/api/src/database/db.ts b/api/src/database/db.ts index 26fec348de..cdc3c1a8b5 100644 --- a/api/src/database/db.ts +++ b/api/src/database/db.ts @@ -209,7 +209,7 @@ export interface IDBConnection { * * const sqlStatement = SQL\`select * from table where id = ${id};\`; * - * const connection = await getDBConnection(req['keycloak_token']); + * const connection = await getDBConnection(req.keycloak_token); * * try { * await connection.open(); @@ -225,7 +225,7 @@ export interface IDBConnection { * @param {object} keycloakToken * @return {*} {IDBConnection} */ -export const getDBConnection = function (keycloakToken: KeycloakUserInformation): IDBConnection { +export const getDBConnection = function (keycloakToken?: KeycloakUserInformation): IDBConnection { if (!keycloakToken) { throw Error('Keycloak token is undefined'); } diff --git a/api/src/middleware/critterbase-proxy.ts b/api/src/middleware/critterbase-proxy.ts index f703ec619c..fd0caa0205 100644 --- a/api/src/middleware/critterbase-proxy.ts +++ b/api/src/middleware/critterbase-proxy.ts @@ -104,7 +104,7 @@ export const getCritterbaseApiHostUrl = () => { export const authorizeAndAuthenticateMiddleware: RequestHandler = async (req, _, next) => { await authenticateRequest(req); - req['authorization_scheme'] = { + req.authorization_scheme = { and: [ { discriminator: 'SystemUser' @@ -165,8 +165,8 @@ export const getCritterbaseProxyMiddleware = () => client.setHeader( 'user', JSON.stringify({ - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier + keycloak_guid: req.system_user?.user_guid, + username: req.system_user?.user_identifier }) ); }, diff --git a/api/src/paths/administrative-activities.ts b/api/src/paths/administrative-activities.ts index 5f83783081..81d8d9b58d 100644 --- a/api/src/paths/administrative-activities.ts +++ b/api/src/paths/administrative-activities.ts @@ -140,7 +140,7 @@ GET.apiDoc = { */ export function getAdministrativeActivities(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { // Only search for specified types if provided, otherwise search all types diff --git a/api/src/paths/administrative-activity.test.ts b/api/src/paths/administrative-activity.test.ts index 2a13a35167..7b2d023dd9 100644 --- a/api/src/paths/administrative-activity.test.ts +++ b/api/src/paths/administrative-activity.test.ts @@ -117,7 +117,7 @@ describe('getAdministrativeActivityStanding', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq['keycloak_token'] = { + mockReq.keycloak_token = { idir_user_guid: 'testguid', identity_provider: 'idir', idir_username: 'testuser', @@ -158,7 +158,7 @@ describe('getAdministrativeActivityStanding', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq['keycloak_token'] = { + mockReq.keycloak_token = { idir_user_guid: 'testguid', identity_provider: 'idir', idir_username: 'testuser', diff --git a/api/src/paths/administrative-activity.ts b/api/src/paths/administrative-activity.ts index 04dec02ae5..77ea91d106 100644 --- a/api/src/paths/administrative-activity.ts +++ b/api/src/paths/administrative-activity.ts @@ -5,6 +5,7 @@ import { HTTP400, HTTP500 } from '../errors/http-error'; import { AdministrativeActivityService } from '../services/administrative-activity-service'; import { getUserGuid } from '../utils/keycloak-utils'; import { getLogger } from '../utils/logger'; +import { getKeycloakTokenFromRequest } from '../utils/request'; const defaultLog = getLogger('paths/administrative-activity-request'); @@ -173,7 +174,8 @@ export function getAdministrativeActivityStanding(): RequestHandler { const connection = getAPIUserDBConnection(); try { - const userGUID = getUserGuid(req['keycloak_token']); + const keycloakToken = getKeycloakTokenFromRequest(req); + const userGUID = getUserGuid(keycloakToken); if (!userGUID) { throw new HTTP400('Failed to identify user'); diff --git a/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/approve.ts b/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/approve.ts index 1f9906e4b4..dbff48abe5 100644 --- a/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/approve.ts +++ b/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/approve.ts @@ -139,7 +139,7 @@ export function approveAccessRequest(): RequestHandler { const roleIds: number[] = req.body.roleIds || []; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/reject.ts b/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/reject.ts index 0ba3b74852..77bb24a6e2 100644 --- a/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/reject.ts +++ b/api/src/paths/administrative-activity/system-access/{administrativeActivityId}/reject.ts @@ -68,7 +68,7 @@ export function rejectAccessRequest(): RequestHandler { return async (req, res) => { const administrativeActivityId = Number(req.params.administrativeActivityId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/animal/index.test.ts b/api/src/paths/animal/index.test.ts index f7cc30846d..7726a0ed4a 100644 --- a/api/src/paths/animal/index.test.ts +++ b/api/src/paths/animal/index.test.ts @@ -5,7 +5,9 @@ import sinonChai from 'sinon-chai'; import { SYSTEM_ROLE } from '../../constants/roles'; import * as db from '../../database/db'; import { HTTPError } from '../../errors/http-error'; +import { SystemUser } from '../../repositories/user-repository'; import { FindCrittersResponse, SurveyCritterService } from '../../services/survey-critter-service'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; import { findAnimals } from './index'; @@ -59,10 +61,10 @@ describe('findAnimals', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] - }; + } as SystemUser; const requestHandler = findAnimals(); @@ -126,10 +128,10 @@ describe('findAnimals', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.PROJECT_CREATOR] - }; + } as SystemUser; const requestHandler = findAnimals(); diff --git a/api/src/paths/animal/index.ts b/api/src/paths/animal/index.ts index ad2d1b229b..4c41fc5075 100644 --- a/api/src/paths/animal/index.ts +++ b/api/src/paths/animal/index.ts @@ -12,6 +12,7 @@ import { makePaginationOptionsFromRequest, makePaginationResponse } from '../../utils/pagination'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/animal'); @@ -184,16 +185,18 @@ export function findAnimals(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'findAnimals' }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); const systemUserId = connection.systemUserId(); + const systemUser = getSystemUserFromRequest(req); + const isUserAdmin = userHasValidRole( [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - req['system_user']['role_names'] + systemUser.role_names ); const filterFields = parseQueryParams(req); diff --git a/api/src/paths/codes.test.ts b/api/src/paths/codes.test.ts index c7704de0b8..2e51289491 100644 --- a/api/src/paths/codes.test.ts +++ b/api/src/paths/codes.test.ts @@ -1,10 +1,10 @@ import chai, { expect } from 'chai'; -import { describe } from 'mocha'; import sinon from 'sinon'; import sinonChai from 'sinon-chai'; import * as db from '../database/db'; import { HTTPError } from '../errors/http-error'; import { CodeService } from '../services/code-service'; +import { KeycloakUserInformation } from '../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../__mocks__/db'; import * as codes from './codes'; @@ -24,7 +24,7 @@ describe('codes', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq.keycloak_token = {}; + mockReq.keycloak_token = {} as KeycloakUserInformation; try { const requestHandler = codes.getAllCodes(); @@ -47,7 +47,7 @@ describe('codes', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq.keycloak_token = {}; + mockReq.keycloak_token = {} as KeycloakUserInformation; const requestHandler = codes.getAllCodes(); @@ -66,7 +66,7 @@ describe('codes', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq.keycloak_token = {}; + mockReq.keycloak_token = {} as KeycloakUserInformation; try { const requestHandler = codes.getAllCodes(); diff --git a/api/src/paths/funding-sources/index.test.ts b/api/src/paths/funding-sources/index.test.ts index 5f48c22b4f..d0e80e2a4a 100644 --- a/api/src/paths/funding-sources/index.test.ts +++ b/api/src/paths/funding-sources/index.test.ts @@ -67,7 +67,7 @@ describe('getFundingSources', () => { agency: null }; // system_user would be set by the authorization-service, if this endpoint was called for real - mockReq['system_user'] = systemUser; + mockReq.system_user = systemUser; const requestHandler = getFundingSources(); @@ -128,7 +128,7 @@ describe('getFundingSources', () => { agency: null }; // system_user would be set by the authorization-service, if this endpoint was called for real - mockReq['system_user'] = systemUser; + mockReq.system_user = systemUser; const requestHandler = getFundingSources(); diff --git a/api/src/paths/funding-sources/index.ts b/api/src/paths/funding-sources/index.ts index dd5fd6d073..c1e351df47 100644 --- a/api/src/paths/funding-sources/index.ts +++ b/api/src/paths/funding-sources/index.ts @@ -3,11 +3,11 @@ import { Operation } from 'express-openapi'; import { SYSTEM_ROLE } from '../../constants/roles'; import { getDBConnection } from '../../database/db'; import { FundingSource, FundingSourceSupplementaryData } from '../../repositories/funding-source-repository'; -import { SystemUser } from '../../repositories/user-repository'; import { authorizeRequestHandler } from '../../request-handlers/security/authorization'; import { FundingSourceService, IFundingSourceSearchParams } from '../../services/funding-source-service'; import { UserService } from '../../services/user-service'; import { getLogger } from '../../utils/logger'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/funding-sources/index'); @@ -113,7 +113,7 @@ GET.apiDoc = { */ export function getFundingSources(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const filterFields: IFundingSourceSearchParams = req.query || {}; try { await connection.open(); @@ -124,8 +124,9 @@ export function getFundingSources(): RequestHandler { await connection.commit(); - const systemUserObject: SystemUser = req['system_user']; - if (!UserService.isAdmin(systemUserObject)) { + const systemUser = getSystemUserFromRequest(req); + + if (!UserService.isAdmin(systemUser)) { // User is not an admin, strip sensitive fields from response response = removeNonAdminFieldsFromFundingSourcesResponse(response); } @@ -260,7 +261,7 @@ POST.apiDoc = { */ export function postFundingSource(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const service = new FundingSourceService(connection); const data = req.body; try { diff --git a/api/src/paths/funding-sources/{fundingSourceId}.ts b/api/src/paths/funding-sources/{fundingSourceId}.ts index b630fef7d7..048e5ffa80 100644 --- a/api/src/paths/funding-sources/{fundingSourceId}.ts +++ b/api/src/paths/funding-sources/{fundingSourceId}.ts @@ -172,7 +172,7 @@ GET.apiDoc = { */ export function getFundingSource(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const fundingSourceId = Number(req.params.fundingSourceId); @@ -309,7 +309,7 @@ PUT.apiDoc = { */ export function putFundingSource(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -408,7 +408,7 @@ DELETE.apiDoc = { */ export function deleteFundingSource(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const fundingSourceId = Number(req.params.fundingSourceId); diff --git a/api/src/paths/observation/index.test.ts b/api/src/paths/observation/index.test.ts index 525d111514..8a15318fe6 100644 --- a/api/src/paths/observation/index.test.ts +++ b/api/src/paths/observation/index.test.ts @@ -6,7 +6,9 @@ import { SYSTEM_ROLE } from '../../constants/roles'; import * as db from '../../database/db'; import { HTTPError } from '../../errors/http-error'; import { ObservationRecordWithSamplingAndSubcountData } from '../../repositories/observation-repository/observation-repository'; +import { SystemUser } from '../../repositories/user-repository'; import { ObservationService } from '../../services/observation-service'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; import { findObservations } from './index'; @@ -79,10 +81,10 @@ describe('findObservations', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] - }; + } as SystemUser; const requestHandler = findObservations(); @@ -172,10 +174,10 @@ describe('findObservations', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.PROJECT_CREATOR] - }; + } as SystemUser; const requestHandler = findObservations(); diff --git a/api/src/paths/observation/index.ts b/api/src/paths/observation/index.ts index de2c23a71f..f90f83be54 100644 --- a/api/src/paths/observation/index.ts +++ b/api/src/paths/observation/index.ts @@ -13,6 +13,7 @@ import { makePaginationOptionsFromRequest, makePaginationResponse } from '../../utils/pagination'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/observation/index'); @@ -169,16 +170,18 @@ export function findObservations(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'getObservations' }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); const systemUserId = connection.systemUserId(); + const systemUser = getSystemUserFromRequest(req); + const isUserAdmin = userHasValidRole( [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - req['system_user']['role_names'] + systemUser.role_names ); const filterFields = parseQueryParams(req); diff --git a/api/src/paths/project/create.ts b/api/src/paths/project/create.ts index 90c1440836..a1e73bb325 100644 --- a/api/src/paths/project/create.ts +++ b/api/src/paths/project/create.ts @@ -87,7 +87,7 @@ POST.apiDoc = { */ export function createProject(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const sanitizedProjectPostData = new PostProjectObject(req.body); diff --git a/api/src/paths/project/index.test.ts b/api/src/paths/project/index.test.ts index 8a31df36b6..d36b2c6d0f 100644 --- a/api/src/paths/project/index.test.ts +++ b/api/src/paths/project/index.test.ts @@ -6,7 +6,9 @@ import { SYSTEM_ROLE } from '../../constants/roles'; import * as db from '../../database/db'; import { HTTPError } from '../../errors/http-error'; import { FindProjectsResponse } from '../../models/project-view'; +import { SystemUser } from '../../repositories/user-repository'; import { ProjectService } from '../../services/project-service'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; import { findProjects } from './index'; @@ -55,10 +57,10 @@ describe('findProjects', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] - }; + } as SystemUser; const requestHandler = findProjects(); @@ -117,10 +119,10 @@ describe('findProjects', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.PROJECT_CREATOR] - }; + } as SystemUser; const requestHandler = findProjects(); diff --git a/api/src/paths/project/index.ts b/api/src/paths/project/index.ts index 609ef2e4d5..8d75b4294c 100644 --- a/api/src/paths/project/index.ts +++ b/api/src/paths/project/index.ts @@ -12,6 +12,7 @@ import { makePaginationOptionsFromRequest, makePaginationResponse } from '../../utils/pagination'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/project/index'); @@ -183,16 +184,18 @@ export function findProjects(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'findProjects' }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); const systemUserId = connection.systemUserId(); + const systemUser = getSystemUserFromRequest(req); + const isUserAdmin = userHasValidRole( [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - req['system_user']['role_names'] + systemUser.role_names ); const filterFields = parseQueryParams(req); diff --git a/api/src/paths/project/{projectId}/attachments/list.ts b/api/src/paths/project/{projectId}/attachments/list.ts index 944d31effe..6c9ce424df 100644 --- a/api/src/paths/project/{projectId}/attachments/list.ts +++ b/api/src/paths/project/{projectId}/attachments/list.ts @@ -131,7 +131,7 @@ export function getAttachments(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'Get attachments list', message: 'params', req_params: req.params }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const projectId = Number(req.params.projectId); try { diff --git a/api/src/paths/project/{projectId}/attachments/report/upload.ts b/api/src/paths/project/{projectId}/attachments/report/upload.ts index fa5ad30e24..8e1083b673 100644 --- a/api/src/paths/project/{projectId}/attachments/report/upload.ts +++ b/api/src/paths/project/{projectId}/attachments/report/upload.ts @@ -155,7 +155,7 @@ export function uploadMedia(): RequestHandler { file: { ...rawMediaFile, buffer: 'Too big to print' } }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -179,8 +179,8 @@ export function uploadMedia(): RequestHandler { // Upload file to S3 const metadata = { filename: rawMediaFile.originalname, - username: (req['auth_payload'] && req['auth_payload'].preferred_username) || '', - email: (req['auth_payload'] && req['auth_payload'].email) || '' + username: (req.keycloak_token && req.keycloak_token.preferred_username) || '', + email: (req.keycloak_token && req.keycloak_token.email) || '' }; await uploadFileToS3(rawMediaFile, upsertResult.key, metadata); diff --git a/api/src/paths/project/{projectId}/attachments/upload.ts b/api/src/paths/project/{projectId}/attachments/upload.ts index 515b4b0ff9..4d5b90c8eb 100644 --- a/api/src/paths/project/{projectId}/attachments/upload.ts +++ b/api/src/paths/project/{projectId}/attachments/upload.ts @@ -130,7 +130,7 @@ export function uploadMedia(): RequestHandler { file: { ...rawMediaFile, buffer: 'Too big to print' } }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -153,8 +153,8 @@ export function uploadMedia(): RequestHandler { // Upload file to S3 const metadata = { filename: rawMediaFile.originalname, - username: (req['auth_payload'] && req['auth_payload'].preferred_username) || '', - email: (req['auth_payload'] && req['auth_payload'].email) || '' + username: (req.keycloak_token && req.keycloak_token.preferred_username) || '', + email: (req.keycloak_token && req.keycloak_token.email) || '' }; await uploadFileToS3(rawMediaFile, upsertResult.key, metadata); diff --git a/api/src/paths/project/{projectId}/attachments/{attachmentId}/delete.ts b/api/src/paths/project/{projectId}/attachments/{attachmentId}/delete.ts index 57664dc131..16c2dbdb05 100644 --- a/api/src/paths/project/{projectId}/attachments/{attachmentId}/delete.ts +++ b/api/src/paths/project/{projectId}/attachments/{attachmentId}/delete.ts @@ -96,7 +96,7 @@ export function deleteAttachment(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'Delete attachment', message: 'params', req_params: req.params }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/attachments/{attachmentId}/getSignedUrl.ts b/api/src/paths/project/{projectId}/attachments/{attachmentId}/getSignedUrl.ts index 0e5b07b373..85ec5c50e2 100644 --- a/api/src/paths/project/{projectId}/attachments/{attachmentId}/getSignedUrl.ts +++ b/api/src/paths/project/{projectId}/attachments/{attachmentId}/getSignedUrl.ts @@ -105,7 +105,7 @@ export function getProjectAttachmentSignedURL(): RequestHandler { req_body: req.body }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/get.ts b/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/get.ts index 82406071ef..f74d77b3c0 100644 --- a/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/get.ts +++ b/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/get.ts @@ -111,7 +111,7 @@ export function getProjectReportDetails(): RequestHandler { req_query: req.query }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/update.ts b/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/update.ts index 5df56a9685..c93706c7f6 100644 --- a/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/update.ts +++ b/api/src/paths/project/{projectId}/attachments/{attachmentId}/metadata/update.ts @@ -133,7 +133,7 @@ export function updateProjectAttachmentMetadata(): RequestHandler { req_body: req.body }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/delete.ts b/api/src/paths/project/{projectId}/delete.ts index b4c40199e8..95869c2d64 100644 --- a/api/src/paths/project/{projectId}/delete.ts +++ b/api/src/paths/project/{projectId}/delete.ts @@ -75,7 +75,7 @@ export function deleteProject(): RequestHandler { throw new HTTP400('Missing required path param: `projectId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const projectId = Number(req.params.projectId); try { diff --git a/api/src/paths/project/{projectId}/participants/index.ts b/api/src/paths/project/{projectId}/participants/index.ts index dc6a382c55..e07c97b0a1 100644 --- a/api/src/paths/project/{projectId}/participants/index.ts +++ b/api/src/paths/project/{projectId}/participants/index.ts @@ -93,7 +93,7 @@ export function getParticipants(): RequestHandler { throw new HTTP400('Missing required param `projectId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const projectId = Number(req.params.projectId); @@ -235,7 +235,7 @@ export function postProjectParticipants(): RequestHandler { throw new HTTP400('Missing required body param `participants`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const participants: IParticipant[] = req.body.participants; diff --git a/api/src/paths/project/{projectId}/participants/self.ts b/api/src/paths/project/{projectId}/participants/self.ts index e3867b715b..fefb2b6aec 100644 --- a/api/src/paths/project/{projectId}/participants/self.ts +++ b/api/src/paths/project/{projectId}/participants/self.ts @@ -70,7 +70,7 @@ export function getSelf(): RequestHandler { throw new HTTP400("Missing required param 'projectId'"); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const projectId = Number(req.params.projectId); diff --git a/api/src/paths/project/{projectId}/participants/{projectParticipationId}.ts b/api/src/paths/project/{projectId}/participants/{projectParticipationId}.ts index 7609cf9f49..79255de1b2 100644 --- a/api/src/paths/project/{projectId}/participants/{projectParticipationId}.ts +++ b/api/src/paths/project/{projectId}/participants/{projectParticipationId}.ts @@ -101,7 +101,7 @@ export function putProjectParticipantRole(): RequestHandler { const projectParticipationId = Number(req.params.projectParticipationId); const roleId = Number(req.body.roleId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -234,7 +234,7 @@ export function deleteProjectParticipant(): RequestHandler { const projectId = Number(req.params.projectId); const projectParticipationId = Number(req.params.projectParticipationId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/create.ts b/api/src/paths/project/{projectId}/survey/create.ts index 4e3ba36794..9fd7b420be 100644 --- a/api/src/paths/project/{projectId}/survey/create.ts +++ b/api/src/paths/project/{projectId}/survey/create.ts @@ -176,7 +176,7 @@ export function createSurvey(): RequestHandler { const sanitizedPostSurveyData = new PostSurveyObject(req.body); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/index.test.ts b/api/src/paths/project/{projectId}/survey/index.test.ts index 8cd4d01167..414684cb6f 100644 --- a/api/src/paths/project/{projectId}/survey/index.test.ts +++ b/api/src/paths/project/{projectId}/survey/index.test.ts @@ -6,6 +6,7 @@ import { getSurveys } from '.'; import * as db from '../../../../database/db'; import { HTTPError } from '../../../../errors/http-error'; import { SurveyService } from '../../../../services/survey-service'; +import { KeycloakUserInformation } from '../../../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db'; chai.use(sinonChai); @@ -24,7 +25,7 @@ describe('survey list', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); - mockReq['keycloak_token'] = {}; + mockReq.keycloak_token = {} as KeycloakUserInformation; mockReq.params = { projectId: '1' }; @@ -72,7 +73,7 @@ describe('survey list', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); const projectId = 3; - mockReq['keycloak_token'] = {}; + mockReq.keycloak_token = {} as KeycloakUserInformation; mockReq.params = { projectId: String(projectId) }; diff --git a/api/src/paths/project/{projectId}/survey/index.ts b/api/src/paths/project/{projectId}/survey/index.ts index 1f6ae88c4a..0a25ea3112 100644 --- a/api/src/paths/project/{projectId}/survey/index.ts +++ b/api/src/paths/project/{projectId}/survey/index.ts @@ -140,7 +140,7 @@ GET.apiDoc = { */ export function getSurveys(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/keyx/upload.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/keyx/upload.ts index f7a2d849c7..d80a89b271 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/keyx/upload.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/keyx/upload.ts @@ -146,7 +146,7 @@ export function uploadKeyxMedia(): RequestHandler { files: { ...rawMediaFile, buffer: 'Too big to print' } }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -164,9 +164,10 @@ export function uploadKeyxMedia(): RequestHandler { } const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() }; + const bctwService = new BctwService(user); const bctwUploadResult = await bctwService.uploadKeyX(rawMediaFile); @@ -182,8 +183,8 @@ export function uploadKeyxMedia(): RequestHandler { const metadata = { filename: rawMediaFile.originalname, - username: req['auth_payload']?.preferred_username ?? '', - email: req['auth_payload']?.email ?? '' + username: req.keycloak_token?.preferred_username ?? '', + email: req.keycloak_token?.email ?? '' }; const result = await uploadFileToS3(rawMediaFile, upsertResult.key, metadata); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/list.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/list.ts index 8bd09bedd0..35b9dc44e6 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/list.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/list.ts @@ -258,7 +258,7 @@ export function getSurveyAttachments(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'Get attachments list', message: 'params', req_params: req.params }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const surveyId = Number(req.params.surveyId); try { diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/report/upload.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/report/upload.ts index 0d3ad3ebf3..c5849ffb56 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/report/upload.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/report/upload.ts @@ -172,7 +172,7 @@ export function uploadMedia(): RequestHandler { files: { ...rawMediaFile, buffer: 'Too big to print' } }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -195,8 +195,8 @@ export function uploadMedia(): RequestHandler { const metadata = { filename: rawMediaFile.originalname, - username: (req['auth_payload'] && req['auth_payload'].preferred_username) || '', - email: (req['auth_payload'] && req['auth_payload'].email) || '' + username: (req.keycloak_token && req.keycloak_token.preferred_username) || '', + email: (req.keycloak_token && req.keycloak_token.email) || '' }; const result = await uploadFileToS3(rawMediaFile, upsertResult.key, metadata); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/upload.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/upload.ts index 939971660f..bc91c9714b 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/upload.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/upload.ts @@ -121,7 +121,7 @@ export function uploadMedia(): RequestHandler { files: { ...rawMediaFile, buffer: 'Too big to print' } }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -144,8 +144,8 @@ export function uploadMedia(): RequestHandler { const metadata = { filename: rawMediaFile.originalname, - username: (req['auth_payload'] && req['auth_payload'].preferred_username) || '', - email: (req['auth_payload'] && req['auth_payload'].email) || '' + username: (req.keycloak_token && req.keycloak_token.preferred_username) || '', + email: (req.keycloak_token && req.keycloak_token.email) || '' }; const result = await uploadFileToS3(rawMediaFile, upsertResult.key, metadata); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/delete.ts index 9a5c0bfe29..ca50846f80 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/delete.ts @@ -102,7 +102,7 @@ export function deleteAttachment(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'Delete attachment', message: 'params', req_params: req.params }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/getSignedUrl.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/getSignedUrl.ts index 84cfb29a7b..d28ae2ab70 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/getSignedUrl.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/getSignedUrl.ts @@ -118,7 +118,7 @@ export function getSurveyAttachmentSignedURL(): RequestHandler { req_body: req.body }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/get.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/get.ts index 4e712c424c..02496f335c 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/get.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/get.ts @@ -121,7 +121,7 @@ export function getSurveyReportDetails(): RequestHandler { req_query: req.query }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/update.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/update.ts index 981f7b8e10..01fbc1077d 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/update.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/attachments/{attachmentId}/metadata/update.ts @@ -139,7 +139,7 @@ export function updateSurveyReportMetadata(): RequestHandler { req_body: req.body }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/delete.ts index d4b0f7d6cc..a20ba8ef23 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/delete.ts @@ -94,7 +94,7 @@ export function removeCrittersFromSurvey(): RequestHandler { const critterIds = req.body.critterIds; const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/import.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/import.ts index ed1e690c52..feb1970c5c 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/import.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/import.ts @@ -128,7 +128,7 @@ export function importCsv(): RequestHandler { const rawFiles = req.files as Express.Multer.File[]; const rawFile = rawFiles[0]; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/index.ts index 13246cee5c..28260460a1 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/index.ts @@ -203,13 +203,8 @@ POST.apiDoc = { export function getCrittersFromSurvey(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -224,8 +219,7 @@ export function getCrittersFromSurvey(): RequestHandler { const critterIds = surveyCritters.map((critter) => String(critter.critterbase_critter_id)); - const critterbaseService = new CritterbaseService(user); - const result = await critterbaseService.getMultipleCrittersByIds(critterIds); + const result = await surveyService.critterbaseService.getMultipleCrittersByIds(critterIds); const critterMap = new Map(); for (const item of result) { @@ -250,19 +244,19 @@ export function getCrittersFromSurvey(): RequestHandler { export function addCritterToSurvey(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const surveyId = Number(req.params.surveyId); let critterId = req.body.critter_id; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const critterbaseService = new CritterbaseService(user); // If request does not include critter ID, create a new critter and use its critter ID diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}.ts index ffa3837792..d4236aa486 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}.ts @@ -85,16 +85,17 @@ PATCH.apiDoc = { export function updateSurveyCritter(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; const critterbaseCritterId = req.body.update.critter_id; const critterId = Number(req.params.critterId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + if (!critterbaseCritterId) { throw new HTTPError(HTTPErrorType.BAD_REQUEST, 400, 'No external critter ID was found.'); } diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/index.ts index 6f23d36217..41c06c9629 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/index.ts @@ -237,11 +237,6 @@ PATCH.apiDoc = { export function deployDevice(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const critterId = Number(req.params.critterId); const newDeploymentId = v4(); // New deployment ID const newDeploymentDevice = { @@ -249,11 +244,16 @@ export function deployDevice(): RequestHandler { deploymentId: newDeploymentId }; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const surveyCritterService = new SurveyCritterService(connection); await surveyCritterService.upsertDeployment(critterId, newDeploymentId); @@ -275,17 +275,18 @@ export function deployDevice(): RequestHandler { export function updateDeployment(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; const critterId = Number(req.params.critterId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const surveyCritterService = new SurveyCritterService(connection); const bctw = new BctwService(user); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/{bctwDeploymentId}.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/{bctwDeploymentId}.ts index d98ee990a1..1538e5e5fd 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/{bctwDeploymentId}.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/deployments/{bctwDeploymentId}.ts @@ -104,19 +104,19 @@ DELETE.apiDoc = { export function deleteDeployment(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const deploymentId = String(req.params.bctwDeploymentId); const critterId = Number(req.params.critterId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const surveyCritterService = new SurveyCritterService(connection); await surveyCritterService.removeDeployment(critterId, deploymentId); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/telemetry.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/telemetry.ts index 7fbf63de05..20fcda05be 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/telemetry.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/critters/{critterId}/telemetry.ts @@ -257,19 +257,19 @@ GET.apiDoc = { export function getCritterTelemetry(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const critterId = Number(req.params.critterId); const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const surveyCritterService = new SurveyCritterService(connection); const surveyCritters = await surveyCritterService.getCrittersInSurvey(surveyId); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/delete.ts index 97cf594a20..2c3e507bea 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/delete.ts @@ -82,7 +82,7 @@ export function deleteSurvey(): RequestHandler { return async (req, res) => { const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/deployments.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/deployments.ts index 211a072412..2c5684841a 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/deployments.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/deployments.ts @@ -86,17 +86,17 @@ GET.apiDoc = { export function getDeploymentsInSurvey(): RequestHandler { return async (req, res) => { - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + const surveyCritterService = new SurveyCritterService(connection); const bctwService = new BctwService(user); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/delete.ts index 00bbfa02b2..f0008aa54b 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/delete.ts @@ -130,7 +130,7 @@ export function deleteSurveyObservations(): RequestHandler { defaultLog.debug({ label: 'deleteSurveyObservations', surveyId }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/environments/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/environments/delete.ts index 2642ee34ef..4ea8c3b84f 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/environments/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/environments/delete.ts @@ -115,7 +115,7 @@ POST.apiDoc = { */ export function deleteObservationEnvironments(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const surveyId = Number(req.params.surveyId); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/index.ts index 49aa7688bd..23ee4994d9 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/index.ts @@ -5,7 +5,7 @@ import { getDBConnection } from '../../../../../../database/db'; import { observervationsWithSubcountDataSchema } from '../../../../../../openapi/schemas/observation'; import { paginationRequestQueryParamSchema } from '../../../../../../openapi/schemas/pagination'; import { authorizeRequestHandler } from '../../../../../../request-handlers/security/authorization'; -import { CritterbaseService } from '../../../../../../services/critterbase-service'; +import { CritterbaseService, ICritterbaseUser } from '../../../../../../services/critterbase-service'; import { InsertUpdateObservations, ObservationService } from '../../../../../../services/observation-service'; import { getLogger } from '../../../../../../utils/logger'; import { ensureCompletePaginationOptions, makePaginationResponse } from '../../../../../../utils/pagination'; @@ -373,7 +373,7 @@ export function getSurveyObservations(): RequestHandler { const paginationOptions: Partial = { page, limit, order, sort }; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -416,7 +416,7 @@ export function putObservations(): RequestHandler { defaultLog.debug({ label: 'insertUpdateSurveyObservations', surveyId }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -425,10 +425,12 @@ export function putObservations(): RequestHandler { const observationRows: InsertUpdateObservations[] = req.body.surveyObservations; - const critterBaseService = new CritterbaseService({ - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }); + const user: ICritterbaseUser = { + keycloak_guid: connection.systemUserGUID(), + username: connection.systemUserIdentifier() + }; + + const critterBaseService = new CritterbaseService(user); // Validate measurement data against fetched measurement definition const isValid = await observationService.validateSurveyObservations(observationRows, critterBaseService); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/measurements/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/measurements/delete.ts index 03767daeb8..59894c1779 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/measurements/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/measurements/delete.ts @@ -112,7 +112,7 @@ export function deleteObservationMeasurements(): RequestHandler { defaultLog.debug({ label: 'deleteObservationMeasurements', surveyId }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/process.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/process.ts index 8c9ad329f2..d4cc63c64a 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/process.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/process.ts @@ -103,7 +103,7 @@ export function processFile(): RequestHandler { const surveyId = Number(req.params.surveyId); const submissionId = req.body.observation_submission_id; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/spatial.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/spatial.ts index b9ac787336..1492421197 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/spatial.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/spatial.ts @@ -250,7 +250,7 @@ export function getSurveyObservationsGeometry(): RequestHandler { defaultLog.debug({ label: 'getSurveyObservationsGeometry', surveyId }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/upload.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/upload.ts index e7eb310283..e154015d3e 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/upload.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/upload.ts @@ -122,7 +122,7 @@ export function uploadMedia(): RequestHandler { throw new HTTP400('Too many files uploaded, expected 1'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const rawMediaFile = rawMediaArray[0]; @@ -151,8 +151,8 @@ export function uploadMedia(): RequestHandler { // Upload file to S3 const metadata = { filename: rawMediaFile.originalname, - username: req['auth_payload']?.preferred_username ?? '', - email: req['auth_payload']?.email ?? '' + username: req.keycloak_token?.preferred_username ?? '', + email: req.keycloak_token?.email ?? '' }; const result = await uploadFileToS3(rawMediaFile, key, metadata); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/{surveyObservationId}/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/{surveyObservationId}/index.ts index 234633a6e6..f7e7fe4a16 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/observations/{surveyObservationId}/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/observations/{surveyObservationId}/index.ts @@ -194,7 +194,7 @@ export function getSurveyObservation(): RequestHandler { defaultLog.debug({ label: 'getSurveyObservation', surveyObservationId }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/participants/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/participants/index.ts index 54b16e4f5c..ea5af0daf4 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/participants/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/participants/index.ts @@ -124,7 +124,7 @@ export function getSurveyParticipants(): RequestHandler { throw new HTTP400('Missing required param `surveyId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const surveyId = Number(req.params.surveyId); @@ -258,7 +258,7 @@ export function createSurveyParticipants(): RequestHandler { throw new HTTP400('Missing required body param `participants`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const participants: ISurveyParticipationPostData[] = req.body.participants; diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/participants/{surveyParticipationId}.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/participants/{surveyParticipationId}.ts index 27b2b2dde8..9e98e5c334 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/participants/{surveyParticipationId}.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/participants/{surveyParticipationId}.ts @@ -107,7 +107,7 @@ export function updateSurveyParticipantRole(): RequestHandler { const surveyParticipationId = Number(req.params.surveyParticipationId); const { surveyJobName } = req.body; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -212,7 +212,7 @@ export function deleteSurveyParticipant(): RequestHandler { const surveyId = Number(req.params.surveyId); const surveyParticipationId = Number(req.params.surveyParticipationId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/delete.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/delete.ts index 87537e4702..ff1463609b 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/delete.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/delete.ts @@ -111,7 +111,7 @@ export function deleteSurveySampleSiteRecords(): RequestHandler { throw new HTTP400('Missing required body `surveySampleSiteIds`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/index.ts index c350b04c75..67dfbfd34c 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/index.ts @@ -267,7 +267,7 @@ export function getSurveySampleLocationRecord(): RequestHandler { throw new HTTP400('Missing required param `surveyId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -563,7 +563,7 @@ export function createSurveySampleSiteRecord(): RequestHandler { throw new HTTP400('Missing required path param `surveyId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const sampleSite: PostSampleLocations = { diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/index.ts index 83be828906..92d1636b01 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/index.ts @@ -228,8 +228,7 @@ PUT.apiDoc = { export function updateSurveySampleSite(): RequestHandler { return async (req, res) => { const surveyId = Number(req.params.surveyId); - - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const sampleSite: UpdateSampleLocationRecord = { @@ -347,7 +346,7 @@ export function deleteSurveySampleSiteRecord(): RequestHandler { throw new HTTP400('Missing required param `surveySampleSiteId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -646,7 +645,7 @@ export function getSurveySampleLocationRecord(): RequestHandler { throw new HTTP400('Missing required param `surveySampleSiteId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/index.ts index 3974cbd455..2350e6d39c 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/index.ts @@ -178,7 +178,7 @@ export function getSurveySampleMethodRecords(): RequestHandler { const surveySampleSiteId = Number(req.params.surveySampleSiteId); const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -315,7 +315,7 @@ export function createSurveySampleSiteRecord(): RequestHandler { const surveyId = Number(req.params.surveyId); const surveySampleSiteId = Number(req.params.surveySampleSiteId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const sampleSiteService = new SampleLocationService(connection); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/index.ts index b6b173a2df..0f5895a10d 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/index.ts @@ -129,7 +129,7 @@ export function updateSurveySampleMethod(): RequestHandler { } const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const sampleMethod: UpdateSampleMethodRecord = { @@ -243,7 +243,7 @@ export function deleteSurveySampleMethodRecord(): RequestHandler { } const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/index.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/index.ts index 17e53873e5..1cc42d05ca 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/index.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/index.ts @@ -165,7 +165,7 @@ export function getSurveySamplePeriodRecords(): RequestHandler { const surveyId = Number(req.params.surveyId); const surveySampleMethodId = Number(req.params.surveySampleMethodId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); @@ -304,7 +304,7 @@ export function createSurveySamplePeriodRecord(): RequestHandler { throw new HTTP400('Missing required body param `samplePeriod`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const samplePeriod: InsertSamplePeriodRecord = { diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/{surveySamplePeriodId}.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/{surveySamplePeriodId}.ts index 4bdaa19774..47eb99df29 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/{surveySamplePeriodId}.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/sample-site/{surveySampleSiteId}/sample-method/{surveySampleMethodId}/sample-period/{surveySamplePeriodId}.ts @@ -147,7 +147,7 @@ export function updateSurveySamplePeriod(): RequestHandler { } const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const samplePeriod: UpdateSamplePeriodRecord = { @@ -280,7 +280,7 @@ export function deleteSurveySamplePeriodRecord(): RequestHandler { throw new HTTP400('Missing required param `surveySamplePeriodId`'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/telemetry/upload.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/telemetry/upload.ts index d04d97b1d8..dab809bebc 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/telemetry/upload.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/telemetry/upload.ts @@ -121,7 +121,7 @@ export function uploadMedia(): RequestHandler { throw new HTTP400('Too many files uploaded, expected 1'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const rawMediaFile = rawMediaArray[0]; @@ -150,8 +150,8 @@ export function uploadMedia(): RequestHandler { // Upload file to S3 const metadata = { filename: rawMediaFile.originalname, - username: req['auth_payload']?.preferred_username ?? '', - email: req['auth_payload']?.email ?? '' + username: req.keycloak_token?.preferred_username ?? '', + email: req.keycloak_token?.email ?? '' }; const result = await uploadFileToS3(rawMediaFile, key, metadata); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/update.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/update.ts index 254344857e..9e3ce263c3 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/update.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/update.ts @@ -183,7 +183,7 @@ export function updateSurvey(): RequestHandler { const sanitizedPutSurveyData = new PutSurveyObject(req.body); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/update/get.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/update/get.ts index 16bbe41a3b..a1ab70d232 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/update/get.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/update/get.ts @@ -171,7 +171,7 @@ export function getSurveyForUpdate(): RequestHandler { return async (req, res) => { const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/survey/{surveyId}/view.ts b/api/src/paths/project/{projectId}/survey/{surveyId}/view.ts index 6fb775be48..6c2cb398d7 100644 --- a/api/src/paths/project/{projectId}/survey/{surveyId}/view.ts +++ b/api/src/paths/project/{projectId}/survey/{surveyId}/view.ts @@ -157,7 +157,7 @@ export function getSurvey(): RequestHandler { return async (req, res) => { const surveyId = Number(req.params.surveyId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/project/{projectId}/update.ts b/api/src/paths/project/{projectId}/update.ts index 209cb0230a..8e87f68f16 100644 --- a/api/src/paths/project/{projectId}/update.ts +++ b/api/src/paths/project/{projectId}/update.ts @@ -185,7 +185,7 @@ GET.apiDoc = { */ export function getProjectForUpdate(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const projectId = Number(req.params?.projectId); @@ -301,7 +301,7 @@ export interface IUpdateProject { */ export function updateProject(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const projectId = Number(req.params?.projectId); diff --git a/api/src/paths/project/{projectId}/view.ts b/api/src/paths/project/{projectId}/view.ts index af1c845de1..73a6e22987 100644 --- a/api/src/paths/project/{projectId}/view.ts +++ b/api/src/paths/project/{projectId}/view.ts @@ -174,7 +174,7 @@ GET.apiDoc = { */ export function viewProject(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/publish/attachment/resubmit.ts b/api/src/paths/publish/attachment/resubmit.ts index ff80f20e5e..aea21f730f 100644 --- a/api/src/paths/publish/attachment/resubmit.ts +++ b/api/src/paths/publish/attachment/resubmit.ts @@ -113,7 +113,7 @@ POST.apiDoc = { // @TODO is this endpoint needed anymore? Do we submit attachments on their own? export function resubmitAttachment(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const resubmitData = req.body; diff --git a/api/src/paths/publish/survey.ts b/api/src/paths/publish/survey.ts index 99fb728161..4059c98833 100644 --- a/api/src/paths/publish/survey.ts +++ b/api/src/paths/publish/survey.ts @@ -125,7 +125,7 @@ POST.apiDoc = { */ export function publishSurvey(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); const { surveyId, data } = req.body; diff --git a/api/src/paths/spatial/regions.ts b/api/src/paths/spatial/regions.ts index 7bd4d05ed7..42e113cd20 100644 --- a/api/src/paths/spatial/regions.ts +++ b/api/src/paths/spatial/regions.ts @@ -110,7 +110,7 @@ POST.apiDoc = { */ export function getRegions(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const features = req.body.features as Feature[]; diff --git a/api/src/paths/standards/taxon/{tsn}/index.ts b/api/src/paths/standards/taxon/{tsn}/index.ts index 1c08b78ead..a46bb597a1 100644 --- a/api/src/paths/standards/taxon/{tsn}/index.ts +++ b/api/src/paths/standards/taxon/{tsn}/index.ts @@ -200,7 +200,7 @@ GET.apiDoc = { export function getSpeciesStandards(): RequestHandler { return async (req, res) => { // TODO: const connection = getAPIUserDBConnection(); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const tsn = Number(req.params.tsn); diff --git a/api/src/paths/survey/index.test.ts b/api/src/paths/survey/index.test.ts index c9a7f502b3..8ecd973ccf 100644 --- a/api/src/paths/survey/index.test.ts +++ b/api/src/paths/survey/index.test.ts @@ -6,7 +6,9 @@ import { SYSTEM_ROLE } from '../../constants/roles'; import * as db from '../../database/db'; import { HTTPError } from '../../errors/http-error'; import { FindSurveysResponse } from '../../models/survey-view'; +import { SystemUser } from '../../repositories/user-repository'; import { SurveyService } from '../../services/survey-service'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; import { findSurveys } from './index'; @@ -59,10 +61,10 @@ describe('findSurveys', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] - }; + } as SystemUser; const requestHandler = findSurveys(); @@ -125,10 +127,10 @@ describe('findSurveys', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.PROJECT_CREATOR] - }; + } as SystemUser; const requestHandler = findSurveys(); diff --git a/api/src/paths/survey/index.ts b/api/src/paths/survey/index.ts index a68d30ded4..e443efa1a7 100644 --- a/api/src/paths/survey/index.ts +++ b/api/src/paths/survey/index.ts @@ -12,6 +12,7 @@ import { makePaginationOptionsFromRequest, makePaginationResponse } from '../../utils/pagination'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/survey/index'); @@ -219,16 +220,18 @@ export function findSurveys(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'findSurveys' }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); const systemUserId = connection.systemUserId(); + const systemUser = getSystemUserFromRequest(req); + const isUserAdmin = userHasValidRole( [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - req['system_user']['role_names'] + systemUser.role_names ); const filterFields = parseQueryParams(req); diff --git a/api/src/paths/telemetry/code.test.ts b/api/src/paths/telemetry/code.test.ts index 1de6816519..442a39d752 100644 --- a/api/src/paths/telemetry/code.test.ts +++ b/api/src/paths/telemetry/code.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../repositories/user-repository'; import { BctwService } from '../../services/bctw-service'; import { getRequestHandlerMocks } from '../../__mocks__/db'; import { getCodeValues } from './code'; @@ -23,6 +24,9 @@ describe('getCodeValues', () => { const mockGetCode = sinon.stub(BctwService.prototype, 'getCode').resolves(mockCodeValues); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getCodeValues(); await requestHandler(mockReq, mockRes, mockNext); @@ -37,6 +41,9 @@ describe('getCodeValues', () => { const mockGetCode = sinon.stub(BctwService.prototype, 'getCode').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getCodeValues(); try { diff --git a/api/src/paths/telemetry/code.ts b/api/src/paths/telemetry/code.ts index 165816e39b..2386b3dc3c 100644 --- a/api/src/paths/telemetry/code.ts +++ b/api/src/paths/telemetry/code.ts @@ -1,7 +1,7 @@ import { RequestHandler } from 'express'; import { Operation } from 'express-openapi'; import { authorizeRequestHandler } from '../../request-handlers/security/authorization'; -import { BctwService, IBctwUser } from '../../services/bctw-service'; +import { BctwService, getBctwUser } from '../../services/bctw-service'; import { getLogger } from '../../utils/logger'; const defaultLog = getLogger('paths/telemetry/code'); @@ -70,12 +70,11 @@ GET.apiDoc = { export function getCodeValues(): RequestHandler { return async (req, res) => { - const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; + const user = getBctwUser(req); + const bctwService = new BctwService(user); const codeHeader = String(req.query.codeHeader); + try { const result = await bctwService.getCode(codeHeader); return res.status(200).json(result); diff --git a/api/src/paths/telemetry/deployments.test.ts b/api/src/paths/telemetry/deployments.test.ts index 0e66886c56..facb4c577c 100644 --- a/api/src/paths/telemetry/deployments.test.ts +++ b/api/src/paths/telemetry/deployments.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../repositories/user-repository'; import { BctwService, IAllTelemetry } from '../../services/bctw-service'; import { getRequestHandlerMocks } from '../../__mocks__/db'; import { getAllTelemetryByDeploymentIds } from './deployments'; @@ -35,6 +36,9 @@ describe('getAllTelemetryByDeploymentIds', () => { .resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getAllTelemetryByDeploymentIds(); await requestHandler(mockReq, mockRes, mockNext); @@ -48,6 +52,9 @@ describe('getAllTelemetryByDeploymentIds', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'getAllTelemetryByDeploymentIds').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getAllTelemetryByDeploymentIds(); try { diff --git a/api/src/paths/telemetry/device/index.test.ts b/api/src/paths/telemetry/device/index.test.ts index 106b471fc0..9bfc486707 100644 --- a/api/src/paths/telemetry/device/index.test.ts +++ b/api/src/paths/telemetry/device/index.test.ts @@ -2,6 +2,7 @@ import Ajv from 'ajv'; import { expect } from 'chai'; import sinon from 'sinon'; import { HTTPError } from '../../../errors/http-error'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; import { POST, upsertDevice } from './index'; @@ -23,6 +24,9 @@ describe('upsertDevice', () => { const mockUpsertDevice = sinon.stub(BctwService.prototype, 'updateDevice'); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = upsertDevice(); await requestHandler(mockReq, mockRes, mockNext); @@ -37,6 +41,8 @@ describe('upsertDevice', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = upsertDevice(); try { await requestHandler(mockReq, mockRes, mockNext); diff --git a/api/src/paths/telemetry/device/index.ts b/api/src/paths/telemetry/device/index.ts index fe6d9efb9f..a166f4944b 100644 --- a/api/src/paths/telemetry/device/index.ts +++ b/api/src/paths/telemetry/device/index.ts @@ -1,7 +1,7 @@ import { RequestHandler } from 'express'; import { Operation } from 'express-openapi'; import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { BctwService, IBctwUser } from '../../../services/bctw-service'; +import { BctwService, getBctwUser } from '../../../services/bctw-service'; import { getLogger } from '../../../utils/logger'; const defaultLog = getLogger('paths/telemetry/device/{deviceId}'); @@ -92,10 +92,8 @@ POST.apiDoc = { export function upsertDevice(): RequestHandler { return async (req, res) => { - const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; + const user = getBctwUser(req); + const bctwService = new BctwService(user); try { const results = await bctwService.updateDevice(req.body); diff --git a/api/src/paths/telemetry/device/{deviceId}.test.ts b/api/src/paths/telemetry/device/{deviceId}.test.ts index 3addc860f5..6c90dff8f5 100644 --- a/api/src/paths/telemetry/device/{deviceId}.test.ts +++ b/api/src/paths/telemetry/device/{deviceId}.test.ts @@ -1,6 +1,7 @@ import Ajv from 'ajv'; import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; import { GET, getDeviceDetails } from './{deviceId}'; @@ -24,6 +25,9 @@ describe('getDeviceDetails', () => { const mockGetKeyXDetails = sinon.stub(BctwService.prototype, 'getKeyXDetails').resolves([]); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getDeviceDetails(); await requestHandler(mockReq, mockRes, mockNext); @@ -39,6 +43,9 @@ describe('getDeviceDetails', () => { const mockGetDeviceDetails = sinon.stub(BctwService.prototype, 'getDeviceDetails').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getDeviceDetails(); try { diff --git a/api/src/paths/telemetry/device/{deviceId}.ts b/api/src/paths/telemetry/device/{deviceId}.ts index 3d9dd11233..7e1f4bfebe 100644 --- a/api/src/paths/telemetry/device/{deviceId}.ts +++ b/api/src/paths/telemetry/device/{deviceId}.ts @@ -1,7 +1,7 @@ import { RequestHandler } from 'express'; import { Operation } from 'express-openapi'; import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { BctwService, IBctwUser } from '../../../services/bctw-service'; +import { BctwService, getBctwUser } from '../../../services/bctw-service'; import { getLogger } from '../../../utils/logger'; const defaultLog = getLogger('paths/telemetry/device/{deviceId}'); @@ -58,10 +58,8 @@ GET.apiDoc = { export function getDeviceDetails(): RequestHandler { return async (req, res) => { - const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; + const user = getBctwUser(req); + const bctwService = new BctwService(user); const deviceId = Number(req.params.deviceId); const deviceMake = String(req.query.make); diff --git a/api/src/paths/telemetry/index.test.ts b/api/src/paths/telemetry/index.test.ts index 9700ce479c..4e53a7fea1 100644 --- a/api/src/paths/telemetry/index.test.ts +++ b/api/src/paths/telemetry/index.test.ts @@ -5,7 +5,9 @@ import sinonChai from 'sinon-chai'; import { SYSTEM_ROLE } from '../../constants/roles'; import * as db from '../../database/db'; import { HTTPError } from '../../errors/http-error'; +import { SystemUser } from '../../repositories/user-repository'; import { FindTelemetryResponse, TelemetryService } from '../../services/telemetry-service'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getMockDBConnection, getRequestHandlerMocks } from '../../__mocks__/db'; import { findTelemetry } from './index'; @@ -57,10 +59,10 @@ describe('findTelemetry', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.SYSTEM_ADMIN] - }; + } as SystemUser; const requestHandler = findTelemetry(); @@ -103,10 +105,10 @@ describe('findTelemetry', () => { sort: undefined, order: undefined }; - mockReq['keycloak_token'] = {}; - mockReq['system_user'] = { + mockReq.keycloak_token = {} as KeycloakUserInformation; + mockReq.system_user = { role_names: [SYSTEM_ROLE.PROJECT_CREATOR] - }; + } as SystemUser; const requestHandler = findTelemetry(); diff --git a/api/src/paths/telemetry/index.ts b/api/src/paths/telemetry/index.ts index a95c986d40..be9e8ae91b 100644 --- a/api/src/paths/telemetry/index.ts +++ b/api/src/paths/telemetry/index.ts @@ -12,6 +12,7 @@ import { makePaginationOptionsFromRequest, makePaginationResponse } from '../../utils/pagination'; +import { getSystemUserFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/telemetry'); @@ -185,16 +186,18 @@ export function findTelemetry(): RequestHandler { return async (req, res) => { defaultLog.debug({ label: 'findTelemetry' }); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); const systemUserId = connection.systemUserId(); + const systemUser = getSystemUserFromRequest(req); + const isUserAdmin = userHasValidRole( [SYSTEM_ROLE.SYSTEM_ADMIN, SYSTEM_ROLE.DATA_ADMINISTRATOR], - req['system_user']['role_names'] + systemUser.role_names ); const filterFields = parseQueryParams(req); diff --git a/api/src/paths/telemetry/manual/delete.test.ts b/api/src/paths/telemetry/manual/delete.test.ts index a0fa851e9c..0bd0c7837c 100644 --- a/api/src/paths/telemetry/manual/delete.test.ts +++ b/api/src/paths/telemetry/manual/delete.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService, IManualTelemetry } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; import { deleteManualTelemetry } from './delete'; @@ -21,6 +22,9 @@ describe('deleteManualTelemetry', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'deleteManualTelemetry').resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = deleteManualTelemetry(); await requestHandler(mockReq, mockRes, mockNext); @@ -34,6 +38,9 @@ describe('deleteManualTelemetry', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'deleteManualTelemetry').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = deleteManualTelemetry(); try { diff --git a/api/src/paths/telemetry/manual/delete.ts b/api/src/paths/telemetry/manual/delete.ts index 2fd6b68173..b1f57b158e 100644 --- a/api/src/paths/telemetry/manual/delete.ts +++ b/api/src/paths/telemetry/manual/delete.ts @@ -2,7 +2,7 @@ import { RequestHandler } from 'express'; import { Operation } from 'express-openapi'; import { manual_telemetry_responses } from '.'; import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { BctwService, IBctwUser } from '../../../services/bctw-service'; +import { BctwService, getBctwUser } from '../../../services/bctw-service'; import { getLogger } from '../../../utils/logger'; const defaultLog = getLogger('paths/telemetry/manual/delete'); @@ -50,10 +50,8 @@ POST.apiDoc = { export function deleteManualTelemetry(): RequestHandler { return async (req, res) => { - const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; + const user = getBctwUser(req); + const bctwService = new BctwService(user); try { const result = await bctwService.deleteManualTelemetry(req.body); diff --git a/api/src/paths/telemetry/manual/deployments.test.ts b/api/src/paths/telemetry/manual/deployments.test.ts index 6e7f831eda..1120703417 100644 --- a/api/src/paths/telemetry/manual/deployments.test.ts +++ b/api/src/paths/telemetry/manual/deployments.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService, IManualTelemetry } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; import { getManualTelemetryByDeploymentIds } from './deployments'; @@ -23,6 +24,9 @@ describe('getManualTelemetryByDeploymentIds', () => { .resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getManualTelemetryByDeploymentIds(); await requestHandler(mockReq, mockRes, mockNext); @@ -36,6 +40,9 @@ describe('getManualTelemetryByDeploymentIds', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'getManualTelemetryByDeploymentIds').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getManualTelemetryByDeploymentIds(); try { diff --git a/api/src/paths/telemetry/manual/index.test.ts b/api/src/paths/telemetry/manual/index.test.ts index ca82e19789..47a5c92cd7 100644 --- a/api/src/paths/telemetry/manual/index.test.ts +++ b/api/src/paths/telemetry/manual/index.test.ts @@ -2,6 +2,7 @@ import Ajv from 'ajv'; import { expect } from 'chai'; import sinon from 'sinon'; import { createManualTelemetry, GET, getManualTelemetry, PATCH, POST, updateManualTelemetry } from '.'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService, IManualTelemetry } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; @@ -30,6 +31,9 @@ describe('manual telemetry endpoints', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'getManualTelemetry').resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getManualTelemetry(); await requestHandler(mockReq, mockRes, mockNext); @@ -44,6 +48,9 @@ describe('manual telemetry endpoints', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'getManualTelemetry').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getManualTelemetry(); try { @@ -66,6 +73,9 @@ describe('manual telemetry endpoints', () => { const mockCreateTelemetry = sinon.stub(BctwService.prototype, 'createManualTelemetry').resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = createManualTelemetry(); await requestHandler(mockReq, mockRes, mockNext); @@ -79,6 +89,9 @@ describe('manual telemetry endpoints', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'createManualTelemetry').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = createManualTelemetry(); try { @@ -101,6 +114,9 @@ describe('manual telemetry endpoints', () => { const mockCreateTelemetry = sinon.stub(BctwService.prototype, 'updateManualTelemetry').resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = updateManualTelemetry(); await requestHandler(mockReq, mockRes, mockNext); @@ -114,6 +130,9 @@ describe('manual telemetry endpoints', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'updateManualTelemetry').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = updateManualTelemetry(); try { diff --git a/api/src/paths/telemetry/manual/process.ts b/api/src/paths/telemetry/manual/process.ts index 35a2889234..cad1db8208 100644 --- a/api/src/paths/telemetry/manual/process.ts +++ b/api/src/paths/telemetry/manual/process.ts @@ -3,7 +3,7 @@ import { Operation } from 'express-openapi'; import { PROJECT_PERMISSION, SYSTEM_ROLE } from '../../../constants/roles'; import { getDBConnection } from '../../../database/db'; import { authorizeRequestHandler } from '../../../request-handlers/security/authorization'; -import { ICritterbaseUser } from '../../../services/critterbase-service'; +import { getBctwUser } from '../../../services/bctw-service'; import { TelemetryService } from '../../../services/telemetry-service'; import { getLogger } from '../../../utils/logger'; @@ -92,12 +92,10 @@ POST.apiDoc = { export function processFile(): RequestHandler { return async (req, res) => { + const user = getBctwUser(req); + const submissionId = req.body.submission_id; - const user: ICritterbaseUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/telemetry/vendor/deployments.test.ts b/api/src/paths/telemetry/vendor/deployments.test.ts index cb9cf157f4..709101c1f5 100644 --- a/api/src/paths/telemetry/vendor/deployments.test.ts +++ b/api/src/paths/telemetry/vendor/deployments.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../../repositories/user-repository'; import { BctwService, IVendorTelemetry } from '../../../services/bctw-service'; import { getRequestHandlerMocks } from '../../../__mocks__/db'; import { getVendorTelemetryByDeploymentIds } from './deployments'; @@ -41,6 +42,9 @@ describe('getVendorTelemetryByDeploymentIds', () => { .resolves(mockTelemetry); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getVendorTelemetryByDeploymentIds(); await requestHandler(mockReq, mockRes, mockNext); @@ -54,6 +58,9 @@ describe('getVendorTelemetryByDeploymentIds', () => { const mockGetTelemetry = sinon.stub(BctwService.prototype, 'getVendorTelemetryByDeploymentIds').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getVendorTelemetryByDeploymentIds(); try { diff --git a/api/src/paths/telemetry/vendors.test.ts b/api/src/paths/telemetry/vendors.test.ts index eb3622797f..ce9c5acc8e 100644 --- a/api/src/paths/telemetry/vendors.test.ts +++ b/api/src/paths/telemetry/vendors.test.ts @@ -1,5 +1,6 @@ import { expect } from 'chai'; import sinon from 'sinon'; +import { SystemUser } from '../../repositories/user-repository'; import { BctwService } from '../../services/bctw-service'; import { getRequestHandlerMocks } from '../../__mocks__/db'; import { getCollarVendors } from './vendors'; @@ -14,6 +15,9 @@ describe('getCollarVendors', () => { const mockGetCollarVendors = sinon.stub(BctwService.prototype, 'getCollarVendors').resolves(mockVendors); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getCollarVendors(); await requestHandler(mockReq, mockRes, mockNext); @@ -29,6 +33,9 @@ describe('getCollarVendors', () => { const mockGetCollarVendors = sinon.stub(BctwService.prototype, 'getCollarVendors').rejects(mockError); const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + + mockReq.system_user = { user_identifier: 'user', user_guid: 'guid' } as SystemUser; + const requestHandler = getCollarVendors(); try { diff --git a/api/src/paths/telemetry/vendors.ts b/api/src/paths/telemetry/vendors.ts index 2f8ad336dd..b0901914ab 100644 --- a/api/src/paths/telemetry/vendors.ts +++ b/api/src/paths/telemetry/vendors.ts @@ -1,7 +1,7 @@ import { RequestHandler } from 'express'; import { Operation } from 'express-openapi'; import { authorizeRequestHandler } from '../../request-handlers/security/authorization'; -import { BctwService, IBctwUser } from '../../services/bctw-service'; +import { BctwService, getBctwUser } from '../../services/bctw-service'; import { getLogger } from '../../utils/logger'; const defaultLog = getLogger('paths/telemetry/vendors'); @@ -61,10 +61,8 @@ GET.apiDoc = { export function getCollarVendors(): RequestHandler { return async (req, res) => { - const user: IBctwUser = { - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier - }; + const user = getBctwUser(req); + const bctwService = new BctwService(user); try { const result = await bctwService.getCollarVendors(); diff --git a/api/src/paths/user/add.test.ts b/api/src/paths/user/add.test.ts index ed4bb716ce..e805ab49fd 100644 --- a/api/src/paths/user/add.test.ts +++ b/api/src/paths/user/add.test.ts @@ -26,6 +26,8 @@ describe('user', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + mockReq.keycloak_token = {} as keycloakUtils.KeycloakUserInformation; + mockReq.body = { userGuid: 'aaaa', userIdentifier: 'username', @@ -70,6 +72,8 @@ describe('user', () => { const { mockReq, mockRes, mockNext } = getRequestHandlerMocks(); + mockReq.keycloak_token = {} as keycloakUtils.KeycloakUserInformation; + mockReq.body = { identitySource: SYSTEM_IDENTITY_SOURCE.IDIR, userIdentifier: 'username', diff --git a/api/src/paths/user/add.ts b/api/src/paths/user/add.ts index abfdb376f2..f8cfea33b6 100644 --- a/api/src/paths/user/add.ts +++ b/api/src/paths/user/add.ts @@ -7,6 +7,7 @@ import { authorizeRequestHandler } from '../../request-handlers/security/authori import { UserService } from '../../services/user-service'; import { getKeycloakSource } from '../../utils/keycloak-utils'; import { getLogger } from '../../utils/logger'; +import { getKeycloakTokenFromRequest } from '../../utils/request'; const defaultLog = getLogger('paths/user/add'); @@ -132,11 +133,11 @@ export function addSystemRoleUser(): RequestHandler { const family_name: string = req.body?.family_name; const role_name: string = req.body?.role_name; - const sourceSystem = getKeycloakSource(req['keycloak_token']); + const keycloakToken = getKeycloakTokenFromRequest(req); - const connection = sourceSystem - ? getServiceClientDBConnection(sourceSystem) - : getDBConnection(req['keycloak_token']); + const sourceSystem = getKeycloakSource(keycloakToken); + + const connection = sourceSystem ? getServiceClientDBConnection(sourceSystem) : getDBConnection(keycloakToken); try { await connection.open(); diff --git a/api/src/paths/user/index.ts b/api/src/paths/user/index.ts index 8a9fae74fe..189c549413 100644 --- a/api/src/paths/user/index.ts +++ b/api/src/paths/user/index.ts @@ -78,7 +78,7 @@ GET.apiDoc = { */ export function getUsers(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/user/list.ts b/api/src/paths/user/list.ts index a4e8ab5f9f..a209757717 100644 --- a/api/src/paths/user/list.ts +++ b/api/src/paths/user/list.ts @@ -70,7 +70,7 @@ GET.apiDoc = { */ export function getUserList(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/user/self.ts b/api/src/paths/user/self.ts index 3ea154a5cf..9a1333f893 100644 --- a/api/src/paths/user/self.ts +++ b/api/src/paths/user/self.ts @@ -66,7 +66,7 @@ GET.apiDoc = { */ export function getUser(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/user/{userId}/delete.ts b/api/src/paths/user/{userId}/delete.ts index f6bccd9db6..750a7df35e 100644 --- a/api/src/paths/user/{userId}/delete.ts +++ b/api/src/paths/user/{userId}/delete.ts @@ -71,7 +71,7 @@ export function removeSystemUser(): RequestHandler { const systemUserId = req.params && Number(req.params.userId); - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/paths/user/{userId}/get.ts b/api/src/paths/user/{userId}/get.ts index e1ad449945..e27063ac01 100644 --- a/api/src/paths/user/{userId}/get.ts +++ b/api/src/paths/user/{userId}/get.ts @@ -157,7 +157,7 @@ GET.apiDoc = { */ export function getUserById(): RequestHandler { return async (req, res) => { - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const userId = Number(req.params.userId); diff --git a/api/src/paths/user/{userId}/projects/get.ts b/api/src/paths/user/{userId}/projects/get.ts index 8eadddf72d..6908d366e2 100644 --- a/api/src/paths/user/{userId}/projects/get.ts +++ b/api/src/paths/user/{userId}/projects/get.ts @@ -133,7 +133,7 @@ export function getAllUserProjects(): RequestHandler { throw new HTTP400('Missing required param: userId'); } - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { const userId = Number(req.params.userId); diff --git a/api/src/paths/user/{userId}/system-roles/update.ts b/api/src/paths/user/{userId}/system-roles/update.ts index e7ea062c5c..eba23a85bc 100644 --- a/api/src/paths/user/{userId}/system-roles/update.ts +++ b/api/src/paths/user/{userId}/system-roles/update.ts @@ -97,7 +97,7 @@ export function updateSystemRolesHandler(): RequestHandler { const userId = Number(req.params.userId); const roles: number[] = req.body.roles; - const connection = getDBConnection(req['keycloak_token']); + const connection = getDBConnection(req.keycloak_token); try { await connection.open(); diff --git a/api/src/request-handlers/security/authentication.ts b/api/src/request-handlers/security/authentication.ts index 5e136aadc3..c3d0fb6afb 100644 --- a/api/src/request-handlers/security/authentication.ts +++ b/api/src/request-handlers/security/authentication.ts @@ -2,6 +2,7 @@ import { Request } from 'express'; import { decode, verify } from 'jsonwebtoken'; import { JwksClient } from 'jwks-rsa'; import { HTTP401 } from '../../errors/http-error'; +import { KeycloakUserInformation } from '../../utils/keycloak-utils'; import { getLogger } from '../../utils/logger'; const defaultLog = getLogger('request-handlers/security/authentication'); @@ -72,7 +73,7 @@ export const authenticateRequest = async function (req: Request): Promise const signingKey = key.getPublicKey(); // Verify token using public signing key - const verifiedToken = verify(tokenString, signingKey, { issuer: [KEYCLOAK_ISSUER] }); + const verifiedToken = verify(tokenString, signingKey, { issuer: [KEYCLOAK_ISSUER] }) as KeycloakUserInformation; if (!verifiedToken) { defaultLog.warn({ label: 'authenticate', message: 'verified token was null' }); @@ -80,7 +81,7 @@ export const authenticateRequest = async function (req: Request): Promise } // Add the verified token to the request for future use, if needed - req['keycloak_token'] = verifiedToken; + req.keycloak_token = verifiedToken; return true; } catch (error) { diff --git a/api/src/request-handlers/security/authorization.ts b/api/src/request-handlers/security/authorization.ts index 2f5bc5ec9c..0dfb0d0ae6 100644 --- a/api/src/request-handlers/security/authorization.ts +++ b/api/src/request-handlers/security/authorization.ts @@ -20,7 +20,8 @@ export type AuthorizationSchemeCallback = (req: Request) => AuthorizationScheme; */ export function authorizeRequestHandler(authorizationSchemeCallback: AuthorizationSchemeCallback): RequestHandler { return async (req, _, next) => { - req['authorization_scheme'] = authorizationSchemeCallback(req); + req.authorization_scheme = authorizationSchemeCallback(req); + const isAuthorized = await authorizeRequest(req); if (!isAuthorized) { @@ -35,7 +36,7 @@ export function authorizeRequestHandler(authorizationSchemeCallback: Authorizati /** * Returns `true` if the user is authorized successfully against the `AuthorizationScheme` in - * `req['authorization_scheme']`, `false` otherwise. + * `req.authorization_scheme`, `false` otherwise. * * Note: System administrators are automatically granted access, regardless of the authorization scheme provided. * @@ -46,7 +47,7 @@ export const authorizeRequest = async (req: Request): Promise => { const connection = getAPIUserDBConnection(); try { - const authorizationScheme: AuthorizationScheme = req['authorization_scheme']; + const authorizationScheme = req.authorization_scheme; if (!authorizationScheme) { // No authorization scheme specified, all authenticated users are authorized @@ -56,8 +57,8 @@ export const authorizeRequest = async (req: Request): Promise => { await connection.open(); const authorizationService = new AuthorizationService(connection, { - systemUser: req['system_user'], - keycloakToken: req['keycloak_token'] + systemUser: req.system_user, + keycloakToken: req.keycloak_token }); const isAuthorized = @@ -65,7 +66,7 @@ export const authorizeRequest = async (req: Request): Promise => { (await authorizationService.executeAuthorizationScheme(authorizationScheme)); // Add the system_user to the request for future use, if needed - req['system_user'] = authorizationService._systemUser; + req.system_user = authorizationService._systemUser; await connection.commit(); diff --git a/api/src/services/bctw-service.ts b/api/src/services/bctw-service.ts index 2860f4add6..136aee209a 100644 --- a/api/src/services/bctw-service.ts +++ b/api/src/services/bctw-service.ts @@ -4,8 +4,9 @@ import FormData from 'form-data'; import { GeometryCollection } from 'geojson'; import { URLSearchParams } from 'url'; import { z } from 'zod'; -import { ApiError, ApiErrorType } from '../errors/api-error'; +import { ApiError, ApiErrorType, ApiGeneralError } from '../errors/api-error'; import { HTTP500 } from '../errors/http-error'; +import { getSystemUserFromRequest } from '../utils/request'; import { KeycloakService } from './keycloak-service'; export const IDeployDevice = z.object({ @@ -179,10 +180,25 @@ export const VENDOR_TELEMETRY = '/vendor-telemetry'; export const DELETE_MANUAL_TELEMETRY = '/manual-telemetry/delete'; export const MANUAL_AND_VENDOR_TELEMETRY = '/all-telemetry'; -export const getBctwUser = (req: Request): IBctwUser => ({ - keycloak_guid: req['system_user']?.user_guid, - username: req['system_user']?.user_identifier -}); +/** + * Safely attempt to retrieve system user fields for BCTW user dependency. + * + * @param {Request} req + * @throws {ApiGeneralError} - Missing required fields + * @returns {IBctwUser} + */ +export const getBctwUser = (req: Request): IBctwUser => { + const systemUser = getSystemUserFromRequest(req); + + if (!systemUser.user_guid || !systemUser.user_identifier) { + throw new ApiGeneralError('System user missing required fields', ['bctw-service->getBctwUser']); + } + + return { + keycloak_guid: systemUser.user_guid, + username: systemUser.user_identifier + }; +}; export class BctwService { user: IBctwUser; diff --git a/api/src/types/express.d.ts b/api/src/types/express.d.ts new file mode 100644 index 0000000000..372c576922 --- /dev/null +++ b/api/src/types/express.d.ts @@ -0,0 +1,45 @@ +import { SystemUser } from '../repositories/user-repository'; +import { AuthorizationScheme } from '../services/authorization-service'; +import { KeycloakUserInformation } from '../utils/keycloak-utils'; + +/** + * Extended Express Request. + * + * Note: Additional properties are populated during different stages of request. + * + * @see utils/request.ts - Request getter functions for extended properties + * + */ +declare module 'express-serve-static-core' { + interface Request { + /** + * Keycloak user JWT token object. + * + * Value is defined for endpoints that specify `Bearer` security with correct JWT token. + * + * @see authentication.ts -> authenticateRequest() + * + */ + keycloak_token?: KeycloakUserInformation; + + /** + * SIMS system user details object. + * + * Value is defined for endpoints that authorize a user successfully against `AuthorizationScheme`. + * + * @see authorization.ts -> authorizeRequest() + * + */ + system_user?: SystemUser; + + /** + * Authorization Scheme object. + * + * Value is defined for endpoints with Authorization. + * + * @see authorization.ts -> authorizeRequestHandler() + * + */ + authorization_scheme?: AuthorizationScheme; + } +} diff --git a/api/src/utils/keycloak-utils.ts b/api/src/utils/keycloak-utils.ts index fbb21e1146..7703ca67ba 100644 --- a/api/src/utils/keycloak-utils.ts +++ b/api/src/utils/keycloak-utils.ts @@ -1,3 +1,4 @@ +import { JwtPayload } from 'jsonwebtoken'; import { SOURCE_SYSTEM, SYSTEM_IDENTITY_SOURCE } from '../constants/database'; /** @@ -73,12 +74,14 @@ export type DatabaseUserInformation = { /** * User information from either an IDIR or BCeID Basic or BCeID Business Keycloak token. */ -export type KeycloakUserInformation = - | IdirUserInformation - | BceidBasicUserInformation - | BceidBusinessUserInformation - | ServiceClientUserInformation - | DatabaseUserInformation; +export type KeycloakUserInformation = JwtPayload & + ( + | IdirUserInformation + | BceidBasicUserInformation + | BceidBusinessUserInformation + | ServiceClientUserInformation + | DatabaseUserInformation + ); /** * Returns the user information guid. diff --git a/api/src/utils/request.ts b/api/src/utils/request.ts new file mode 100644 index 0000000000..a20e79fe86 --- /dev/null +++ b/api/src/utils/request.ts @@ -0,0 +1,41 @@ +import { Request } from 'express'; +import { SystemUser } from '../repositories/user-repository'; +import { KeycloakUserInformation } from './keycloak-utils'; + +/** + * Get keycloak token from request or throw error. + * + * Value is defined for endpoints that specify `Bearer` security with correct JWT token. + * + * @see authentication.ts -> authenticateRequest() + * + * @param {Request} req - Express Request + * @throws {Error} - Missing keycloak token + * @returns {KeycloakUserInformation} + */ +export const getKeycloakTokenFromRequest = (req: Request): KeycloakUserInformation => { + if (!req.keycloak_token) { + throw new Error('Request missing keycloak token. Must be authenticated.'); + } + + return req.keycloak_token; +}; + +/** + * Get system user from request of throw error. + * + * Value is defined for endpoints that authorize a user successfully against `AuthorizationScheme`. + * + * @see authorization.ts -> authorizeRequest() + * + * @param {Request} req - Express Request + * @throws {Error} - Missing system user + * @returns {SystemUser} + */ +export const getSystemUserFromRequest = (req: Request): SystemUser => { + if (!req.system_user) { + throw new Error('Request missing system user. Must be authorized.'); + } + + return req.system_user; +};