-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
BHBC-985: System user role updates (#240)
* BHBC-985: System user role updates - Add support for authorization in API - Add support for database connections with the API's user in context - Fixed bug where BCEID users weren't being parsed correctly in db.ts - Add systemRole support to useKeycloakWrapper.tsx - Add endpoint to fetch user information - Incorporated Charlie's context updates to allow the API to be set as the database context user. * BHBC-985: Updates to sql and api role checks * - Review updates - Remove system role checking in APi until we are ready to enforce it
- Loading branch information
Showing
24 changed files
with
821 additions
and
126 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,5 @@ | ||
export enum SYSTEM_USER_TYPE { | ||
export enum SYSTEM_IDENTITY_SOURCE { | ||
DATABASE = 'DATABASE', | ||
IDIR = 'IDIR', | ||
BCEID = 'BCEID' | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { expect } from 'chai'; | ||
import { describe } from 'mocha'; | ||
import { UserObject } from './user'; | ||
|
||
describe('UserObject', () => { | ||
describe('No values provided', () => { | ||
let data: UserObject; | ||
|
||
before(() => { | ||
data = new UserObject((null as unknown) as any); | ||
}); | ||
|
||
it('sets id', function () { | ||
expect(data.id).to.equal(null); | ||
}); | ||
|
||
it('sets user_identifier', function () { | ||
expect(data.user_identifier).to.equal(null); | ||
}); | ||
|
||
it('sets role_names', function () { | ||
expect(data.role_names).to.eql([]); | ||
}); | ||
}); | ||
|
||
describe('valid values provided, no roles', () => { | ||
let data: UserObject; | ||
|
||
const userObject = { id: 1, user_identifier: 'test name', role_names: [] }; | ||
|
||
before(() => { | ||
data = new UserObject(userObject); | ||
}); | ||
|
||
it('sets id', function () { | ||
expect(data.id).to.equal(1); | ||
}); | ||
|
||
it('sets user_identifier', function () { | ||
expect(data.user_identifier).to.equal('test name'); | ||
}); | ||
|
||
it('sets role_names', function () { | ||
expect(data.role_names).to.eql([]); | ||
}); | ||
}); | ||
|
||
describe('valid values provided', () => { | ||
let data: UserObject; | ||
|
||
const userObject = { id: 1, user_identifier: 'test name', role_names: ['role 1', 'role 2'] }; | ||
|
||
before(() => { | ||
data = new UserObject(userObject); | ||
}); | ||
|
||
it('sets id', function () { | ||
expect(data.id).to.equal(1); | ||
}); | ||
|
||
it('sets user_identifier', function () { | ||
expect(data.user_identifier).to.equal('test name'); | ||
}); | ||
|
||
it('sets role_names', function () { | ||
expect(data.role_names).to.eql(['role 1', 'role 2']); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { getLogger } from '../utils/logger'; | ||
|
||
const defaultLog = getLogger('models/user'); | ||
|
||
export class UserObject { | ||
id: number; | ||
user_identifier: string; | ||
role_names: string[]; | ||
|
||
constructor(obj?: any) { | ||
defaultLog.debug({ label: 'UserObject', message: 'params', obj }); | ||
|
||
this.id = obj?.id || null; | ||
this.user_identifier = obj?.user_identifier || null; | ||
this.role_names = (obj?.role_names?.length && obj.role_names) || []; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,93 @@ | ||
import { RequestHandler } from 'express'; | ||
import { Operation } from 'express-openapi'; | ||
import { READ_ROLES } from '../constants/roles'; | ||
import { getDBConnection } from '../database/db'; | ||
import { HTTP400 } from '../errors/CustomError'; | ||
import { getUserByIdSQL } from '../queries/users/user-queries'; | ||
import { getLogger } from '../utils/logger'; | ||
import { logRequest } from '../utils/path-utils'; | ||
|
||
const defaultLog = getLogger('paths/user'); | ||
|
||
export const GET: Operation = [logRequest('paths/user', 'GET'), getUser()]; | ||
|
||
GET.apiDoc = { | ||
description: 'Get user details for the currently authenticated user.', | ||
tags: ['user'], | ||
security: [ | ||
{ | ||
Bearer: READ_ROLES | ||
} | ||
], | ||
responses: { | ||
200: { | ||
description: 'User details for the currently authenticated user.', | ||
content: { | ||
'application/json': { | ||
schema: { | ||
title: 'User Response Object', | ||
type: 'object', | ||
properties: { | ||
// TODO needs finalizing (here and in the user-queries.ts SQL) | ||
} | ||
} | ||
} | ||
} | ||
}, | ||
400: { | ||
$ref: '#/components/responses/400' | ||
}, | ||
401: { | ||
$ref: '#/components/responses/401' | ||
}, | ||
403: { | ||
$ref: '#/components/responses/401' | ||
}, | ||
500: { | ||
$ref: '#/components/responses/500' | ||
}, | ||
default: { | ||
$ref: '#/components/responses/default' | ||
} | ||
} | ||
}; | ||
|
||
/** | ||
* Get a user by its user identifier. | ||
* | ||
* @returns {RequestHandler} | ||
*/ | ||
function getUser(): RequestHandler { | ||
return async (req, res) => { | ||
const connection = getDBConnection(req['keycloak_token']); | ||
|
||
try { | ||
await connection.open(); | ||
|
||
const systemUserId = connection.systemUserId(); | ||
|
||
if (!systemUserId) { | ||
throw new HTTP400('Failed to identify system user ID'); | ||
} | ||
|
||
const getUserSQLStatement = getUserByIdSQL(systemUserId); | ||
|
||
if (!getUserSQLStatement) { | ||
throw new HTTP400('Failed to build SQL get statement'); | ||
} | ||
|
||
const response = await connection.query(getUserSQLStatement.text, getUserSQLStatement.values); | ||
|
||
await connection.commit(); | ||
|
||
const result = (response && response.rows && response.rows[0]) || null; | ||
|
||
return res.status(200).json(result); | ||
} catch (error) { | ||
defaultLog.debug({ label: 'getUser', message: 'error', error }); | ||
throw error; | ||
} finally { | ||
connection.release(); | ||
} | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { expect } from 'chai'; | ||
import { describe } from 'mocha'; | ||
import { getUserByIdSQL, getUserByUserIdentifierSQL } from './user-queries'; | ||
|
||
describe('getUserByUserIdentifierSQL', () => { | ||
it('returns null response when null userIdentifier provided', () => { | ||
const response = getUserByUserIdentifierSQL((null as unknown) as string); | ||
|
||
expect(response).to.be.null; | ||
}); | ||
|
||
it('returns non null response when valid userIdentifier provided', () => { | ||
const response = getUserByUserIdentifierSQL('aUserName'); | ||
|
||
expect(response).to.not.be.null; | ||
}); | ||
}); | ||
|
||
describe('getUserByIdSQL', () => { | ||
it('returns null response when null userId provided', () => { | ||
const response = getUserByIdSQL((null as unknown) as number); | ||
|
||
expect(response).to.be.null; | ||
}); | ||
|
||
it('returns non null response when valid userId provided', () => { | ||
const response = getUserByIdSQL(1); | ||
|
||
expect(response).to.not.be.null; | ||
}); | ||
}); |
Oops, something went wrong.