Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BHBC_950 #643

Merged
merged 20 commits into from
Nov 19, 2021
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions api/src/models/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ const defaultLog = getLogger('models/user');
export class UserObject {
id: number;
user_identifier: string;
user_record_end_date: string;
role_ids: number[];
role_names: string[];

Expand All @@ -13,6 +14,7 @@ export class UserObject {

this.id = obj?.id || null;
this.user_identifier = obj?.user_identifier || null;
this.user_record_end_date = obj?.user_record_end_date || null;
this.role_ids = (obj?.role_ids?.length && obj.role_ids) || [];
this.role_names = (obj?.role_names?.length && obj.role_names) || [];
}
Expand Down
24 changes: 22 additions & 2 deletions api/src/paths/access-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { PROJECT_ROLE } from '../constants/roles';
import { getDBConnection } from '../database/db';
import { getDBConnection, IDBConnection } from '../database/db';
import { HTTP400, HTTP500 } from '../errors/CustomError';
import { UserObject } from '../models/user';
import { getUserByUserIdentifierSQL } from '../queries/users/user-queries';
import { getUserByUserIdentifierSQL, activateSystemUserSQL } from '../queries/users/user-queries';
import { authorizeRequestHandler } from '../request-handlers/security/authorization';
import { getLogger } from '../utils/logger';
import { updateAdministrativeActivity } from './administrative-activity';
Expand Down Expand Up @@ -166,6 +166,10 @@ export function updateAccessRequest(): RequestHandler {
throw new HTTP500('Failed to get or add system user');
}

if (userData.record_end_date) {
await activateDeactivatedSystemUser(userObject.id, connection);
}

// Filter out any system roles that have already been added to the user
const rolesIdsToAdd = roleIds.filter((roleId) => !userObject.role_ids.includes(roleId));

Expand All @@ -188,3 +192,19 @@ export function updateAccessRequest(): RequestHandler {
}
};
}

export const activateDeactivatedSystemUser = async (systemUserId: number, connection: IDBConnection): Promise<any> => {
const sqlStatement = activateSystemUserSQL(systemUserId);

if (!sqlStatement) {
throw new HTTP400('Failed to build SQL update statement');
}

const response = await connection.query(sqlStatement.text, sqlStatement.values);

if (!response) {
throw new HTTP400('Failed to activate system user');
}

return response?.rows?.[0] || null;
};
5 changes: 3 additions & 2 deletions api/src/paths/user.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { getLogger } from '../utils/logger';

const defaultLog = getLogger('paths/user');

export const POST: Operation = [
export const DELETE: Operation = [
authorizeRequestHandler(() => {
return {
and: [
Expand All @@ -23,7 +23,7 @@ export const POST: Operation = [
addUser()
];

POST.apiDoc = {
DELETE.apiDoc = {
description: 'Add a new system user.',
tags: ['user'],
security: [
Expand Down Expand Up @@ -122,6 +122,7 @@ export function addUser(): RequestHandler {
return res.send(200);
} catch (error) {
defaultLog.error({ label: 'getUser', message: 'error', error });
await connection.rollback();
throw error;
} finally {
connection.release();
Expand Down
162 changes: 162 additions & 0 deletions api/src/paths/user/{userId}/delete.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
'use strict';

import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { SYSTEM_ROLE } from '../../../constants/roles';
import { getDBConnection } from '../../../database/db';
import { HTTP400, HTTP500 } from '../../../errors/CustomError';
import {
deActivateSystemUserSQL,
deleteAllProjectRolesSQL,
deleteAllSystemRolesSQL,
getUserByIdSQL
} from '../../../queries/users/user-queries';
import { authorizeRequestHandler } from '../../../request-handlers/security/authorization';
import { getLogger } from '../../../utils/logger';

const defaultLog = getLogger('paths/user/{userId}/delete');

export const DELETE: Operation = [
authorizeRequestHandler(() => {
return {
and: [
{
validSystemRoles: [SYSTEM_ROLE.SYSTEM_ADMIN],
discriminator: 'SystemRole'
}
]
};
}),
removeSystemUser()
];

DELETE.apiDoc = {
description: 'Remove a user from the system.',
tags: ['user'],
security: [
{
Bearer: []
}
],
parameters: [
{
in: 'path',
name: 'userId',
schema: {
type: 'number'
},
required: true
}
],
responses: {
200: {
description: 'Remove system user from system OK.'
},
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'
}
}
};

export function removeSystemUser(): RequestHandler {
return async (req, res) => {
defaultLog.debug({ label: 'removeSystemUser', message: 'params', req_params: req.params });

const userId = (req.params && Number(req.params.userId)) || null;

if (!userId) {
throw new HTTP400('Missing required path param: userId');
}

const connection = getDBConnection(req['keycloak_token']);

try {
// Get the system user
const getUserSQLStatement = getUserByIdSQL(userId);

if (!getUserSQLStatement) {
throw new HTTP400('Failed to build SQL get statement');
}

await connection.open();

const getUserResponse = await connection.query(getUserSQLStatement.text, getUserSQLStatement.values);

const userResult = (getUserResponse && getUserResponse.rowCount && getUserResponse.rows[0]) || null;

if (!userResult) {
throw new HTTP400('Failed to get system user');
}

if (userResult.user_record_end_date) {
throw new HTTP400('The system user is not active');
}

const deleteAllProjectRolesSqlStatement = deleteAllProjectRolesSQL(userId);

if (!deleteAllProjectRolesSqlStatement) {
throw new HTTP400('Failed to build SQL delete statement for deleting system roles');
}

const deleteProjectRolesResponse = await connection.query(
deleteAllProjectRolesSqlStatement.text,
deleteAllProjectRolesSqlStatement.values
);

if (!deleteProjectRolesResponse) {
throw new HTTP400('Failed to the project project roles');
}

const deleteSystemRoleSqlStatement = deleteAllSystemRolesSQL(userId);

if (!deleteSystemRoleSqlStatement) {
throw new HTTP400('Failed to build SQL delete statement for deleting system roles');
}

const deleteSystemRolesResponse = await connection.query(
deleteSystemRoleSqlStatement.text,
deleteSystemRoleSqlStatement.values
);

if (!deleteSystemRolesResponse) {
throw new HTTP400('Failed to delete the user system roles');
}

const deleteSystemUserSqlStatement = deActivateSystemUserSQL(userId);

if (!deleteSystemUserSqlStatement) {
throw new HTTP400('Failed to build SQL delete statement to deactivate system user');
}

const response = await connection.query(deleteSystemUserSqlStatement.text, deleteSystemUserSqlStatement.values);

await connection.commit();

const result = (response && response.rowCount) || null;

if (!result) {
throw new HTTP500('Failed to remove user from the system');
}

return res.status(200).send();
} catch (error) {
defaultLog.error({ label: 'removeSystemUser', message: 'error', error });
await connection.rollback();
throw error;
} finally {
connection.release();
}
};
}
19 changes: 18 additions & 1 deletion api/src/paths/users.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,24 @@ GET.apiDoc = {
title: 'User Response Object',
type: 'object',
properties: {
// TODO needs finalizing (here and in the user-queries.ts SQL)
id: {
type: 'number'
},
user_identifier: {
type: 'string'
},
role_ids: {
type: 'array',
items: {
type: 'string'
}
},
role_names: {
type: 'array',
items: {
type: 'string'
}
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,24 +27,32 @@ export const getProjectParticipationBySystemUserSQL = (
}

const sqlStatement = SQL`
SELECT
pp.project_id,
pp.system_user_id,
array_remove(array_agg(pr.project_role_id), NULL) AS project_role_ids,
array_remove(array_agg(pr.name), NULL) AS project_role_names
FROM
project_participation pp
LEFT JOIN
project_role pr
ON
pp.project_role_id = pr.project_role_id
WHERE
pp.project_id = ${projectId}
AND
pp.system_user_id = ${systemUserId}
GROUP BY
pp.project_id,
pp.system_user_id;
SELECT
pp.project_id,
pp.system_user_id,
su.record_end_date,
array_remove(array_agg(pr.project_role_id), NULL) AS project_role_ids,
array_remove(array_agg(pr.name), NULL) AS project_role_names
FROM
project_participation pp
LEFT JOIN
project_role pr
ON
pp.project_role_id = pr.project_role_id
LEFT JOIN
system_user su
ON
pp.system_user_id = su.system_user_id
WHERE
pp.project_id = ${projectId}
AND
pp.system_user_id = ${systemUserId}
AND
su.record_end_date is NULL
GROUP BY
pp.project_id,
pp.system_user_id,
su.record_end_date ;
`;

defaultLog.info({
Expand Down
Loading