Skip to content

Commit

Permalink
Merge branch 'dataset_security_feature' into SIMSBIOHUB-379
Browse files Browse the repository at this point in the history
  • Loading branch information
curtisupshall committed Dec 8, 2023
1 parent 85a2aa4 commit eb023bd
Show file tree
Hide file tree
Showing 47 changed files with 1,453 additions and 560 deletions.
13 changes: 13 additions & 0 deletions api/src/database/db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,19 @@ export const defaultPoolConfig: pg.PoolConfig = {
pg.types.setTypeParser(pg.types.builtins.DATE, (stringValue: string) => {
return stringValue; // 1082 for `DATE` type
});
// Adding a TIMESTAMP type parser to keep all dates used in the system consistent
pg.types.setTypeParser(pg.types.builtins.TIMESTAMP, (stringValue: string) => {
return stringValue; // 1082 for `TIMESTAMP` type
});
// Adding a TIMESTAMPTZ type parser to keep all dates used in the system consistent
pg.types.setTypeParser(pg.types.builtins.TIMESTAMPTZ, (stringValue: string) => {
return stringValue; // 1082 for `DATE` type
});
// NUMERIC column types return as strings to maintain precision. Converting this to a float so it is usable by the system
// Explanation of why Numeric returns as a string: https://github.com/brianc/node-postgres/issues/811
pg.types.setTypeParser(pg.types.builtins.NUMERIC, (stringValue: string) => {
return parseFloat(stringValue);
});

// singleton pg pool instance used by the api
let DBPool: pg.Pool | undefined;
Expand Down
2 changes: 1 addition & 1 deletion api/src/openapi/schemas/http-responses.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const defaultErrorResponses = {
$ref: '#/components/responses/401'
},
403: {
$ref: '#/components/responses/401'
$ref: '#/components/responses/403'
},
409: {
$ref: '#/components/responses/409'
Expand Down
103 changes: 0 additions & 103 deletions api/src/paths/administrative/review/list.ts

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@ 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 { SubmissionService } from '../../../services/submission-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../__mocks__/db';
import * as list from './list';
import { getUnreviewedSubmissions } from '.';
import * as db from '../../../../database/db';
import { HTTPError } from '../../../../errors/http-error';
import { SubmissionService } from '../../../../services/submission-service';
import { getMockDBConnection, getRequestHandlerMocks } from '../../../../__mocks__/db';

chai.use(sinonChai);

Expand All @@ -26,7 +26,7 @@ describe('list', () => {

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

const requestHandler = list.getDatasetsForReview();
const requestHandler = getUnreviewedSubmissions();

try {
await requestHandler(mockReq, mockRes, mockNext);
Expand All @@ -36,7 +36,7 @@ describe('list', () => {
}
});

it('should return 200 after update is completed', async () => {
it('should return an array of unreviewed submission objects', async () => {
const dbConnectionObj = getMockDBConnection({
commit: sinon.stub(),
rollback: sinon.stub(),
Expand All @@ -47,13 +47,16 @@ describe('list', () => {

const { mockReq, mockRes, mockNext } = getRequestHandlerMocks();

const mock = sinon.stub(SubmissionService.prototype, 'getDatasetsForReview').resolves();
const getUnreviewedSubmissionsStub = sinon
.stub(SubmissionService.prototype, 'getUnreviewedSubmissions')
.resolves([]);

const requestHandler = list.getDatasetsForReview();
const requestHandler = getUnreviewedSubmissions();

await requestHandler(mockReq, mockRes, mockNext);

expect(mock).to.have.been.calledOnce;
expect(getUnreviewedSubmissionsStub).to.have.been.calledOnce;
expect(mockRes.statusValue).to.equal(200);
expect(mockRes.jsonValue).to.eql([]);
});
});
122 changes: 122 additions & 0 deletions api/src/paths/administrative/submission/unreviewed/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { SYSTEM_ROLE } from '../../../../constants/roles';
import { getDBConnection } from '../../../../database/db';
import { defaultErrorResponses } from '../../../../openapi/schemas/http-responses';
import { authorizeRequestHandler } from '../../../../request-handlers/security/authorization';
import { SubmissionService } from '../../../../services/submission-service';
import { getLogger } from '../../../../utils/logger';

const defaultLog = getLogger('paths/administrative/submission/unreviewed');

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

GET.apiDoc = {
description: 'Get a list of submissions that need security review (are unreviewed).',
tags: ['admin'],
security: [
{
Bearer: []
}
],
responses: {
200: {
description: 'List of submissions that need security review.',
content: {
'application/json': {
schema: {
type: 'array',
items: {
type: 'object',
properties: {
submission_id: {
type: 'integer',
minimum: 1
},
uuid: {
type: 'string',
format: 'uuid'
},
security_review_timestamp: {
type: 'string',
nullable: true
},
source_system: {
type: 'string'
},
name: {
type: 'string',
maxLength: 200
},
description: {
type: 'string',
maxLength: 3000
},
create_date: {
type: 'string'
},
create_user: {
type: 'integer',
minimum: 1
},
update_date: {
type: 'string',
nullable: true
},
update_user: {
type: 'integer',
minimum: 1,
nullable: true
},
revision_count: {
type: 'integer',
minimum: 0
}
}
}
}
}
}
},
...defaultErrorResponses
}
};

/**
* Get all unreviewed submissions.
*
* @returns {RequestHandler}
*/
export function getUnreviewedSubmissions(): RequestHandler {
return async (req, res) => {
const connection = getDBConnection(req['keycloak_token']);

try {
await connection.open();

await connection.commit();

const service = new SubmissionService(connection);
const response = await service.getUnreviewedSubmissions();

return res.status(200).json(response);
} catch (error) {
defaultLog.error({ label: 'getUnreviewedSubmissions', message: 'error', error });
throw error;
} finally {
connection.release();
}
};
}
77 changes: 77 additions & 0 deletions api/src/paths/dataset/{datasetId}/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import { RequestHandler } from 'express';
import { Operation } from 'express-openapi';
import { getAPIUserDBConnection, getDBConnection } from '../../../database/db';
import { defaultErrorResponses } from '../../../openapi/schemas/http-responses';
import { DatasetService } from '../../../services/dataset-service';
import { getLogger } from '../../../utils/logger';

const defaultLog = getLogger('paths/dataset/{datasetId}');

export const GET: Operation = [getDatasetInformation()];

GET.apiDoc = {
description: 'retrieves dataset data from the submission table',
tags: ['eml'],
security: [
{
OptionalBearer: []
}
],
parameters: [
{
description: 'dataset uuid',
in: 'path',
name: 'datasetId',
schema: {
type: 'string',
format: 'uuid'
},
required: true
}
],
responses: {
200: {
description: 'Dataset metadata response object.',
content: {
'application/json': {
schema: {
type: 'object'
//TODO: add schema
}
}
}
},
...defaultErrorResponses
}
};

/**
* Retrieves dataset data from the submission table.
*
* @returns {RequestHandler}
*/
export function getDatasetInformation(): RequestHandler {
return async (req, res) => {
const connection = req['keycloak_token'] ? getDBConnection(req['keycloak_token']) : getAPIUserDBConnection();

const datasetId = String(req.params.datasetId);

try {
await connection.open();

const datasetService = new DatasetService(connection);

const result = await datasetService.getDatasetByDatasetUUID(datasetId);

await connection.commit();

res.status(200).json(result);
} catch (error) {
defaultLog.error({ label: 'getMetadataByDatasetId', message: 'error', error });
await connection.rollback();
throw error;
} finally {
connection.release();
}
};
}
4 changes: 2 additions & 2 deletions api/src/repositories/artifact-repository.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export const Artifact = ArtifactMetadata.extend({
uuid: z.string().uuid(),
key: z.string(),
foi_reason: z.boolean().nullable().optional(),
security_review_timestamp: z.date().nullable().optional(),
create_date: z.date().optional()
security_review_timestamp: z.string().nullable().optional(),
create_date: z.string().optional()
});

export type Artifact = z.infer<typeof Artifact>;
Expand Down
Loading

0 comments on commit eb023bd

Please sign in to comment.