From 4efec280826200e7565a4fc212f12298349404ac Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Mon, 11 Sep 2023 13:29:39 -0400 Subject: [PATCH 1/7] Added auth --- src/server.ts | 62 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/src/server.ts b/src/server.ts index 36e3e0395..c5d46ffa8 100644 --- a/src/server.ts +++ b/src/server.ts @@ -34,6 +34,8 @@ import { database, up } from 'migrate-mongo'; import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; import schema from './schemas/index'; +import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; +import jwt from 'jsonwebtoken'; let secrets: any = {}; let server: Server; @@ -153,10 +155,70 @@ let server: Server; /** * Start Graphql server. */ + + type GlobalGqlContext = { + isUserRequest: boolean; + egoToken: string; + Authorization: string; + userJwtData: EgoJwtData | undefined; + dataLoaders: {}; + }; + + /* const decodeToken = (egoPublicKey: string) => (egoJwt: string): EgoJwtData => { + const decoded = jwt.verify(egoJwt, egoPublicKey, { algorithms: ['RS256'] }); + if (typeof decoded == 'string' || decoded === null) { + throw Error('Unexpected JWT Format'); + } else { + return decoded; + } + };*/ + + const decodeToken = (egoJwt: string, egoPublicKey: string): EgoJwtData => { + const decoded = jwt.verify(egoJwt, egoPublicKey, { algorithms: ['RS256'] }); + if (typeof decoded == 'string' || decoded === null) { + throw Error('Unexpected JWT Format'); + } else { + return decoded; + } + }; + + /* const apolloServer = new ApolloServer({ + schema, + });*/ + /* const { url } = await startStandaloneServer(apolloServer, { + listen: { port: app.get('graphqlPort') }, + });*/ + const apolloServer = new ApolloServer({ schema, }); + const { url } = await startStandaloneServer(apolloServer, { + context: async ({ req, res }) => { + // Get the user token from the headers. + const authHeader = req.headers.authorization; + let userJwtData: EgoJwtData | undefined = undefined; + try { + if (authHeader) { + const jwt = authHeader.replace('Bearer ', ''); + userJwtData = decodeToken( + jwt, + 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lOqMuPLCVusc6szklNXQL1FHhSkEgR7An+8BllBqTsRHM4bRYosseGFCbYPn8r8FsWuMDtxp0CwTyMQR2PCbJ740DdpbE1KC6jAfZxqcBete7gP0tooJtbvnA6X4vNpG4ukhtUoN9DzNOO0eqMU0Rgyy5HjERdYEWkwTNB30i9I+nHFOSj4MGLBSxNlnuo3keeomCRgtimCx+L/K3HNo0QHTG1J7RzLVAchfQT0lu3pUJ8kB+UM6/6NG+fVyysJyRZ9gadsr4gvHHckw8oUBp2tHvqBEkEdY+rt1Mf5jppt7JUV7HAPLB/qR5jhALY2FX/8MN+lPLmb/nLQQichVQIDAQAB', + ); + } + } catch (err) { + userJwtData = undefined; + } + + // Add the user to the context + return { + isUserRequest: true, + egoToken: (authHeader || '').split('Bearer ').join(''), + Authorization: `Bearer ${(authHeader || '').replace(/^Bearer[\s]*/, '')}` || '', + userJwtData, + dataLoaders: {}, + }; + }, listen: { port: app.get('graphqlPort') }, }); From ba9708f12d7fa2ba439e43e741177dd7d3f8dc97 Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Tue, 12 Sep 2023 16:16:28 -0400 Subject: [PATCH 2/7] Added the clear clinical submission query and auth related changes --- src/app.ts | 10 +++ src/dictionary/api.ts | 4 +- .../clearClinicalSubmissionResolver.ts | 43 +++++++++++++ .../clinicalSubmissionDataResolver.ts | 6 +- src/schemas/gqlTypeDefs.ts | 12 ++++ src/schemas/resolvers.ts | 4 ++ src/schemas/utils.ts | 34 ++++++++++ src/server.ts | 62 ------------------- src/submission/submission-api.ts | 34 +++++++--- src/utils.ts | 11 +++- 10 files changed, 145 insertions(+), 75 deletions(-) create mode 100644 src/schemas/clinical-resolvers/clearClinicalSubmissionResolver.ts diff --git a/src/app.ts b/src/app.ts index c8260d2f1..eb234bdcc 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,6 +27,7 @@ import * as swaggerUi from 'swagger-ui-express'; import { getHealth, Status } from './app-health'; import { loggerFor } from './logger'; import * as middleware from './middleware'; +import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; import dataRouter from './routes/data'; import registrationRouter from './routes/registration'; @@ -44,6 +45,15 @@ process.title = 'clinical'; // Create Express server const app = express(); + +export type GlobalGqlContext = { + isUserRequest: boolean; + egoToken: string; + Authorization: string; + userJwtData: EgoJwtData | undefined; + dataLoaders: {}; +}; + app.set('port', process.env.PORT || 3000); app.set('graphqlPort', process.env.GRAPHQLPORT || 3001); app.use(bodyParser.json()); diff --git a/src/dictionary/api.ts b/src/dictionary/api.ts index cd12cdb2d..d6f780207 100644 --- a/src/dictionary/api.ts +++ b/src/dictionary/api.ts @@ -33,7 +33,7 @@ class SchemaController { async update(req: Request, res: Response) { const version: string = req.body.version; const sync: boolean = req.query.sync; - const initiator = ControllerUtils.getUserFromToken(req); + const initiator = ControllerUtils.getUserFromRequest(req); const migration = await manager.instance().updateSchemaVersion(version, initiator, sync); return res.status(200).send(migration); } @@ -49,7 +49,7 @@ class SchemaController { @HasFullWriteAccess() async dryRunUpdate(req: Request, res: Response) { const version: string = req.body.version; - const initiator = ControllerUtils.getUserFromToken(req); + const initiator = ControllerUtils.getUserFromRequest(req); const migration = await manager.instance().dryRunSchemaUpgrade(version, initiator); return res.status(200).send(migration); } diff --git a/src/schemas/clinical-resolvers/clearClinicalSubmissionResolver.ts b/src/schemas/clinical-resolvers/clearClinicalSubmissionResolver.ts new file mode 100644 index 000000000..b05519373 --- /dev/null +++ b/src/schemas/clinical-resolvers/clearClinicalSubmissionResolver.ts @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2023 The Ontario Institute for Cancer Research. All rights reserved + * + * This program and the accompanying materials are made available under the terms of + * the GNU Affero General Public License v3.0. You should have received a copy of the + * GNU Affero General Public License along with this program. + * If not, see . + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY + * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED + * TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER + * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +import { GlobalGqlContext } from '../../app'; +import submissionAPI from '../../submission/submission-api'; +import { convertClinicalSubmissionDataToGql } from '../utils'; + +const clearClinicalSubmissionResolver = { + clearClinicalSubmission: async ( + obj: unknown, + args: { programShortName: string; fileType: string; version: string }, + contextValue: any, + ) => { + const { programShortName, fileType, version } = args; + const response = await submissionAPI.clearFileDataFromActiveSubmission( + programShortName, + fileType, + version, + (contextValue).egoToken, + ); + return convertClinicalSubmissionDataToGql(programShortName, { + submission: response, + }); + }, +}; + +export default clearClinicalSubmissionResolver; diff --git a/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts b/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts index 1e5671c3c..a17f0b867 100644 --- a/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts +++ b/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts @@ -21,7 +21,7 @@ import submissionAPI from '../../submission/submission-api'; import get from 'lodash/get'; import { ActiveClinicalSubmission } from '../../submission/submission-entities'; import { DeepReadonly } from 'deep-freeze'; -import { convertClinicalFileErrorToGql, convertClinicalSubmissionEntityToGql } from '../utils'; +import { convertClinicalSubmissionDataToGql } from '../utils'; import { getClinicalEntitiesData } from '../../dictionary/api'; const clinicalSubmissionResolver = { @@ -35,7 +35,7 @@ const clinicalSubmissionResolver = { }, }; -const convertClinicalSubmissionDataToGql = ( +/*const convertClinicalSubmissionDataToGql = ( programShortName: string, data: { submission: DeepReadonly | undefined; @@ -64,6 +64,6 @@ const convertClinicalSubmissionDataToGql = ( }, fileErrors: fileErrors?.map(convertClinicalFileErrorToGql), }; -}; +};*/ export default clinicalSubmissionResolver; diff --git a/src/schemas/gqlTypeDefs.ts b/src/schemas/gqlTypeDefs.ts index 074fddb50..7663f465c 100644 --- a/src/schemas/gqlTypeDefs.ts +++ b/src/schemas/gqlTypeDefs.ts @@ -37,6 +37,18 @@ const typeDefs = gql` clinicalSubmissions(programShortName: String!): ClinicalSubmissionData! } + type Mutation { + """ + Clear Clinical Submission + fileType is optional, if it is not provided all fileTypes will be cleared. The values for fileType are the same as the file names from each template (ex. donor, specimen) + """ + clearClinicalSubmission( + programShortName: String! + version: String! + fileType: String + ): ClinicalSubmissionData! + } + scalar DateTime """ diff --git a/src/schemas/resolvers.ts b/src/schemas/resolvers.ts index 4584ff905..e8d89b0f8 100644 --- a/src/schemas/resolvers.ts +++ b/src/schemas/resolvers.ts @@ -1,6 +1,7 @@ import clinicalRegistrationResolver from './clinical-resolvers/clinicalRegistrationData'; import clinicalSearchResultResolver from './clinical-resolvers/clinicalSearchResults'; import clinicalSubmissionResolver from './clinical-resolvers/clinicalSubmissionDataResolver'; +import clearClinicalSubmissionResolver from './clinical-resolvers/clearClinicalSubmissionResolver'; const resolvers = { Query: { @@ -8,6 +9,9 @@ const resolvers = { ...clinicalSearchResultResolver, ...clinicalSubmissionResolver, }, + Mutation: { + ...clearClinicalSubmissionResolver, + }, }; export default resolvers; diff --git a/src/schemas/utils.ts b/src/schemas/utils.ts index 3a1d40bb2..c4016a561 100644 --- a/src/schemas/utils.ts +++ b/src/schemas/utils.ts @@ -19,10 +19,12 @@ import get from 'lodash/get'; import { + ActiveClinicalSubmission, SubmissionValidationError, SubmissionValidationUpdate, } from '../submission/submission-entities'; import { DeepReadonly } from 'deep-freeze'; +import { getClinicalEntitiesData } from '../dictionary/api'; const ARRAY_DELIMITER_CHAR = '|'; @@ -209,6 +211,37 @@ const convertClinicalSubmissionUpdateToGql = (updateData: UpdateData) => { }; }; +const convertClinicalSubmissionDataToGql = ( + programShortName: string, + data: { + submission: DeepReadonly | undefined; + batchErrors?: { message: string; batchNames: string[]; code: string }[]; + }, +) => { + const submission = get(data, 'submission', {} as Partial); + const fileErrors = get(data, 'batchErrors', [] as typeof data.batchErrors); + const clinicalEntities = get(submission, 'clinicalEntities'); + return { + id: submission?._id || undefined, + programShortName, + state: submission?.state || undefined, + version: submission?.version || undefined, + updatedBy: submission?.updatedBy || undefined, + updatedAt: submission?.updatedAt ? submission.updatedAt : undefined, + clinicalEntities: async () => { + const clinicalSubmissionTypeList = await getClinicalEntitiesData('false'); // to confirm for true or false + const filledClinicalEntities = clinicalSubmissionTypeList.map(clinicalType => ({ + clinicalType, + ...(clinicalEntities ? clinicalEntities[clinicalType.name] : {}), + })); + return filledClinicalEntities.map(clinicalEntity => + convertClinicalSubmissionEntityToGql(clinicalEntity?.clinicalType.name, clinicalEntity), + ); + }, + fileErrors: fileErrors?.map(convertClinicalFileErrorToGql), + }; +}; + export { convertClinicalRecordToGql, convertRegistrationErrorToGql, @@ -216,4 +249,5 @@ export { convertRegistrationStatsToGql, RegistrationErrorData, convertClinicalSubmissionEntityToGql, + convertClinicalSubmissionDataToGql, }; diff --git a/src/server.ts b/src/server.ts index c5d46ffa8..36e3e0395 100644 --- a/src/server.ts +++ b/src/server.ts @@ -34,8 +34,6 @@ import { database, up } from 'migrate-mongo'; import { ApolloServer } from '@apollo/server'; import { startStandaloneServer } from '@apollo/server/standalone'; import schema from './schemas/index'; -import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; -import jwt from 'jsonwebtoken'; let secrets: any = {}; let server: Server; @@ -155,70 +153,10 @@ let server: Server; /** * Start Graphql server. */ - - type GlobalGqlContext = { - isUserRequest: boolean; - egoToken: string; - Authorization: string; - userJwtData: EgoJwtData | undefined; - dataLoaders: {}; - }; - - /* const decodeToken = (egoPublicKey: string) => (egoJwt: string): EgoJwtData => { - const decoded = jwt.verify(egoJwt, egoPublicKey, { algorithms: ['RS256'] }); - if (typeof decoded == 'string' || decoded === null) { - throw Error('Unexpected JWT Format'); - } else { - return decoded; - } - };*/ - - const decodeToken = (egoJwt: string, egoPublicKey: string): EgoJwtData => { - const decoded = jwt.verify(egoJwt, egoPublicKey, { algorithms: ['RS256'] }); - if (typeof decoded == 'string' || decoded === null) { - throw Error('Unexpected JWT Format'); - } else { - return decoded; - } - }; - - /* const apolloServer = new ApolloServer({ - schema, - });*/ - /* const { url } = await startStandaloneServer(apolloServer, { - listen: { port: app.get('graphqlPort') }, - });*/ - const apolloServer = new ApolloServer({ schema, }); - const { url } = await startStandaloneServer(apolloServer, { - context: async ({ req, res }) => { - // Get the user token from the headers. - const authHeader = req.headers.authorization; - let userJwtData: EgoJwtData | undefined = undefined; - try { - if (authHeader) { - const jwt = authHeader.replace('Bearer ', ''); - userJwtData = decodeToken( - jwt, - 'MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0lOqMuPLCVusc6szklNXQL1FHhSkEgR7An+8BllBqTsRHM4bRYosseGFCbYPn8r8FsWuMDtxp0CwTyMQR2PCbJ740DdpbE1KC6jAfZxqcBete7gP0tooJtbvnA6X4vNpG4ukhtUoN9DzNOO0eqMU0Rgyy5HjERdYEWkwTNB30i9I+nHFOSj4MGLBSxNlnuo3keeomCRgtimCx+L/K3HNo0QHTG1J7RzLVAchfQT0lu3pUJ8kB+UM6/6NG+fVyysJyRZ9gadsr4gvHHckw8oUBp2tHvqBEkEdY+rt1Mf5jppt7JUV7HAPLB/qR5jhALY2FX/8MN+lPLmb/nLQQichVQIDAQAB', - ); - } - } catch (err) { - userJwtData = undefined; - } - - // Add the user to the context - return { - isUserRequest: true, - egoToken: (authHeader || '').split('Bearer ').join(''), - Authorization: `Bearer ${(authHeader || '').replace(/^Bearer[\s]*/, '')}` || '', - userJwtData, - dataLoaders: {}, - }; - }, listen: { port: app.get('graphqlPort') }, }); diff --git a/src/submission/submission-api.ts b/src/submission/submission-api.ts index 4e2a3e31c..a81c9090f 100644 --- a/src/submission/submission-api.ts +++ b/src/submission/submission-api.ts @@ -35,6 +35,7 @@ import { HasFullWriteAccess, HasProgramWriteAccess } from '../decorators'; import _ from 'lodash'; import { batchErrorMessage } from './submission-error-messages'; import * as fs from 'fs'; +import { GlobalGqlContext } from '../app'; const L = loggerFor(__filename); const fsPromises = fs.promises; @@ -62,7 +63,7 @@ class SubmissionController { return; } const programId = req.params.programId; - const creator = ControllerUtils.getUserFromToken(req); + const creator = ControllerUtils.getUserFromRequest(req); const file = req.file; let records: ReadonlyArray; try { @@ -136,7 +137,7 @@ class SubmissionController { return; } - const user = ControllerUtils.getUserFromToken(req); + const user = ControllerUtils.getUserFromRequest(req); const newClinicalData: NewClinicalEntity[] = []; const tsvParseErrors: SubmissionBatchError[] = []; const clinicalFiles = req.files as Express.Multer.File[]; @@ -191,7 +192,7 @@ class SubmissionController { async validateActiveSubmission(req: Request, res: Response) { if (await submissionSystemIsDisabled(res)) return; const { versionId, programId } = req.params; - const updater = ControllerUtils.getUserFromToken(req); + const updater = ControllerUtils.getUserFromRequest(req); const result = await submission.operations.validateMultipleClinical({ versionId, programId, @@ -207,7 +208,7 @@ class SubmissionController { async clearFileFromActiveSubmission(req: Request, res: Response) { if (await submissionSystemIsDisabled(res)) return; const { programId, versionId, fileType } = req.params; - const updater = ControllerUtils.getUserFromToken(req); + const updater = ControllerUtils.getUserFromRequest(req); L.debug(`Entering clearFileFromActiveSubmission: ${{ programId, versionId, fileType }}`); const updatedSubmission = await submission.operations.clearSubmissionData({ programId, @@ -219,11 +220,30 @@ class SubmissionController { return res.status(200).send(updatedSubmission || {}); } + // @HasProgramWriteAccess((programId: string) => programId) + async clearFileDataFromActiveSubmission( + programId: string, + fileType: string, + versionId: string, + token: string, + ) { + const updater = ControllerUtils.getUserFromToken(token); + L.debug(`Entering clearFileFromActiveSubmission: ${{ programId, versionId, fileType }}`); + const updatedSubmission = await submission.operations.clearSubmissionData({ + programId, + versionId, + fileType, + updater, + }); + // Handle case where submission was cleared and is now undefined + return updatedSubmission; + } + @HasProgramWriteAccess((req: Request) => req.params.programId) async commitActiveSubmission(req: Request, res: Response) { if (await submissionSystemIsDisabled(res)) return; const { versionId, programId } = req.params; - const updater = ControllerUtils.getUserFromToken(req); + const updater = ControllerUtils.getUserFromRequest(req); const activeSubmission = await submission2Clinical.commitClinicalSubmission({ versionId, programId, @@ -247,7 +267,7 @@ class SubmissionController { async reopenActiveSubmission(req: Request, res: Response) { if (await submissionSystemIsDisabled(res)) return; const { versionId, programId } = req.params; - const updater = ControllerUtils.getUserFromToken(req); + const updater = ControllerUtils.getUserFromRequest(req); const activeSubmission = await submission.operations.reopenClinicalSubmission({ versionId, programId, @@ -299,7 +319,7 @@ class SubmissionController { const samplesSubmitterIds = req.query.sampleSubmitterIds && req.query.sampleSubmitterIds.split(','); const dryRun = req.query.dryRun === 'false' ? false : true; - const updater = ControllerUtils.getUserFromToken(req); + const updater = ControllerUtils.getUserFromRequest(req); L.info( `Delete registered samples called, caller ${updater}, ids: ${samplesSubmitterIds}, programId: ${programId}`, ); diff --git a/src/utils.ts b/src/utils.ts index 6f0ad85d5..5c018ce83 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -136,7 +136,7 @@ export namespace ControllerUtils { }; // checks authHeader + decoded jwt and returns the user name - export const getUserFromToken = (req: Request): string => { + export const getUserFromRequest = (req: Request): string => { const authHeader = req.headers.authorization; if (!authHeader) { throw new Error("can't get here without auth header"); @@ -147,6 +147,15 @@ export namespace ControllerUtils { } return decoded.context.user.firstName + ' ' + decoded.context.user.lastName; }; + + // checks authHeader + decoded jwt and returns the user name + export const getUserFromToken = (token: string) => { + const decoded = jwt.decode(token) as any; + if (!decoded || !decoded.context || !decoded.context.user) { + throw new Error('invalid token structure'); + } + return decoded.context.user.firstName + ' ' + decoded.context.user.lastName; + }; } export namespace DonorUtils { From f175fc8ef1e925633f2eb0f8cf2aae0871f1545a Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Tue, 12 Sep 2023 16:27:41 -0400 Subject: [PATCH 3/7] auth changes --- src/server.ts | 41 +++++++++++++++++++++++++++++--- src/submission/submission-api.ts | 4 +++- 2 files changed, 41 insertions(+), 4 deletions(-) diff --git a/src/server.ts b/src/server.ts index 36e3e0395..c652ceb21 100644 --- a/src/server.ts +++ b/src/server.ts @@ -16,6 +16,8 @@ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ +import { token } from 'morgan'; + console.time('boot time'); // Has to import config before any other import uses the configurations import { AppConfig, RxNormDbConfig, KafkaConfigurations } from './config'; @@ -29,11 +31,17 @@ import { Server } from 'http'; // we import here to allow configs to fully load import * as bootstrap from './bootstrap'; import app from './app'; +import { GlobalGqlContext } from './app'; import { database, up } from 'migrate-mongo'; -import { ApolloServer } from '@apollo/server'; -import { startStandaloneServer } from '@apollo/server/standalone'; +import { ApolloServer, ContextFunction } from '@apollo/server'; +import { + StandaloneServerContextFunctionArgument, + startStandaloneServer, +} from '@apollo/server/standalone'; import schema from './schemas/index'; +import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; +import jwt from 'jsonwebtoken'; let secrets: any = {}; let server: Server; @@ -153,10 +161,37 @@ let server: Server; /** * Start Graphql server. */ - const apolloServer = new ApolloServer({ + + const context: ContextFunction< + [StandaloneServerContextFunctionArgument], + GlobalGqlContext + > = async ({ req, res }) => { + // Get the user token from the headers. + const authHeader = req.headers.authorization; + let userJwtData: EgoJwtData | undefined = undefined; + try { + if (authHeader) { + const jwt = authHeader.replace('Bearer ', ''); + } + } catch (err) { + userJwtData = undefined; + } + // Add the user to the context + return { + isUserRequest: true, + egoToken: (authHeader || '').split('Bearer ').join(''), + Authorization: `Bearer ${(authHeader || '').replace(/^Bearer[\s]*!/, '')}` || '', + userJwtData, + dataLoaders: {}, + }; + }; + + const apolloServer = new ApolloServer({ schema, }); + const { url } = await startStandaloneServer(apolloServer, { + context, listen: { port: app.get('graphqlPort') }, }); diff --git a/src/submission/submission-api.ts b/src/submission/submission-api.ts index a81c9090f..7c536c1ad 100644 --- a/src/submission/submission-api.ts +++ b/src/submission/submission-api.ts @@ -227,8 +227,10 @@ class SubmissionController { versionId: string, token: string, ) { + // res: Response = undefined; + // if (await submissionSystemIsDisabled(res)) return; const updater = ControllerUtils.getUserFromToken(token); - L.debug(`Entering clearFileFromActiveSubmission: ${{ programId, versionId, fileType }}`); + L.debug(`Entering clearFileDataFromActiveSubmission: ${{ programId, versionId, fileType }}`); const updatedSubmission = await submission.operations.clearSubmissionData({ programId, versionId, From 9d5bea305bf1316fb8b1a0e7d18904eea0242a89 Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Tue, 12 Sep 2023 16:39:24 -0400 Subject: [PATCH 4/7] some code refactoring --- src/server.ts | 1 - src/submission/submission-api.ts | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/server.ts b/src/server.ts index c652ceb21..ac0d2c98e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -41,7 +41,6 @@ import { } from '@apollo/server/standalone'; import schema from './schemas/index'; import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; -import jwt from 'jsonwebtoken'; let secrets: any = {}; let server: Server; diff --git a/src/submission/submission-api.ts b/src/submission/submission-api.ts index 7c536c1ad..f94845187 100644 --- a/src/submission/submission-api.ts +++ b/src/submission/submission-api.ts @@ -227,8 +227,8 @@ class SubmissionController { versionId: string, token: string, ) { - // res: Response = undefined; - // if (await submissionSystemIsDisabled(res)) return; + const submissionSystemDisabled = await persistedConfig.getSubmissionDisabledState(); + if (submissionSystemDisabled) return; const updater = ControllerUtils.getUserFromToken(token); L.debug(`Entering clearFileDataFromActiveSubmission: ${{ programId, versionId, fileType }}`); const updatedSubmission = await submission.operations.clearSubmissionData({ From a1655db05f5ca3d1f3135784643b1f0d34b6ec0e Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Tue, 12 Sep 2023 17:31:04 -0400 Subject: [PATCH 5/7] some code refactoring --- src/app.ts | 2 -- src/server.ts | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/app.ts b/src/app.ts index eb234bdcc..c9069127e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -27,7 +27,6 @@ import * as swaggerUi from 'swagger-ui-express'; import { getHealth, Status } from './app-health'; import { loggerFor } from './logger'; import * as middleware from './middleware'; -import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; import dataRouter from './routes/data'; import registrationRouter from './routes/registration'; @@ -50,7 +49,6 @@ export type GlobalGqlContext = { isUserRequest: boolean; egoToken: string; Authorization: string; - userJwtData: EgoJwtData | undefined; dataLoaders: {}; }; diff --git a/src/server.ts b/src/server.ts index ac0d2c98e..67bb774b1 100644 --- a/src/server.ts +++ b/src/server.ts @@ -40,7 +40,7 @@ import { startStandaloneServer, } from '@apollo/server/standalone'; import schema from './schemas/index'; -import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; +// import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; let secrets: any = {}; let server: Server; @@ -167,7 +167,7 @@ let server: Server; > = async ({ req, res }) => { // Get the user token from the headers. const authHeader = req.headers.authorization; - let userJwtData: EgoJwtData | undefined = undefined; + let userJwtData = undefined; try { if (authHeader) { const jwt = authHeader.replace('Bearer ', ''); @@ -180,7 +180,6 @@ let server: Server; isUserRequest: true, egoToken: (authHeader || '').split('Bearer ').join(''), Authorization: `Bearer ${(authHeader || '').replace(/^Bearer[\s]*!/, '')}` || '', - userJwtData, dataLoaders: {}, }; }; From 15990796567a4aa6ce4625beca424dfd0e16a533 Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Tue, 12 Sep 2023 17:42:19 -0400 Subject: [PATCH 6/7] removed commented code --- .../clinicalSubmissionDataResolver.ts | 31 ------------------- 1 file changed, 31 deletions(-) diff --git a/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts b/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts index a17f0b867..420b44a17 100644 --- a/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts +++ b/src/schemas/clinical-resolvers/clinicalSubmissionDataResolver.ts @@ -35,35 +35,4 @@ const clinicalSubmissionResolver = { }, }; -/*const convertClinicalSubmissionDataToGql = ( - programShortName: string, - data: { - submission: DeepReadonly | undefined; - batchErrors?: { message: string; batchNames: string[]; code: string }[]; - }, -) => { - const submission = get(data, 'submission', {} as Partial); - const fileErrors = get(data, 'batchErrors', [] as typeof data.batchErrors); - const clinicalEntities = get(submission, 'clinicalEntities'); - return { - id: submission?._id || undefined, - programShortName, - state: submission?.state || undefined, - version: submission?.version || undefined, - updatedBy: submission?.updatedBy || undefined, - updatedAt: submission?.updatedAt ? submission.updatedAt : undefined, - clinicalEntities: async () => { - const clinicalSubmissionTypeList = await getClinicalEntitiesData('false'); // to confirm for true or false - const filledClinicalEntities = clinicalSubmissionTypeList.map(clinicalType => ({ - clinicalType, - ...(clinicalEntities ? clinicalEntities[clinicalType.name] : {}), - })); - return filledClinicalEntities.map(clinicalEntity => - convertClinicalSubmissionEntityToGql(clinicalEntity?.clinicalType.name, clinicalEntity), - ); - }, - fileErrors: fileErrors?.map(convertClinicalFileErrorToGql), - }; -};*/ - export default clinicalSubmissionResolver; From cc829b0cc4f9f4a69a0eaa413e6fd4df384e78bd Mon Sep 17 00:00:00 2001 From: UmmulkiramR Date: Thu, 14 Sep 2023 13:27:17 -0400 Subject: [PATCH 7/7] added reference to EgoJwtData --- package-lock.json | 20 ++++++++++++++++++++ package.json | 1 + src/app.ts | 2 ++ src/server.ts | 5 +++-- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index f97969d42..272911546 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "dependencies": { "@apollo/server": "4.0.0", "@apollo/subgraph": "2.5.2", + "@icgc-argo/ego-token-utils": "^8.2.0", "@overturebio-stack/lectern-client": "1.4.0", "@types/mongoose-paginate-v2": "^1.3.11", "adm-zip": "^0.4.16", @@ -982,6 +983,17 @@ "graphql": "^0.8.0 || ^0.9.0 || ^0.10.0 || ^0.11.0 || ^0.12.0 || ^0.13.0 || ^14.0.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" } }, + "node_modules/@icgc-argo/ego-token-utils": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@icgc-argo/ego-token-utils/-/ego-token-utils-8.3.0.tgz", + "integrity": "sha512-11Qhuh7LbHsrgZJ6Z4ZxYLY4IINkarzu/WamNMggeDK9oY8R5n5CaimB86sRc1ceHnRpzbMY6S82JovTmAOxIw==", + "dependencies": { + "jsonwebtoken": "^8.5.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@josephg/resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", @@ -9943,6 +9955,14 @@ "integrity": "sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==", "requires": {} }, + "@icgc-argo/ego-token-utils": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/@icgc-argo/ego-token-utils/-/ego-token-utils-8.3.0.tgz", + "integrity": "sha512-11Qhuh7LbHsrgZJ6Z4ZxYLY4IINkarzu/WamNMggeDK9oY8R5n5CaimB86sRc1ceHnRpzbMY6S82JovTmAOxIw==", + "requires": { + "jsonwebtoken": "^8.5.1" + } + }, "@josephg/resolvable": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@josephg/resolvable/-/resolvable-1.0.1.tgz", diff --git a/package.json b/package.json index ba06ef358..78458c403 100644 --- a/package.json +++ b/package.json @@ -102,6 +102,7 @@ "@apollo/server": "4.0.0", "@overturebio-stack/lectern-client": "1.4.0", "@types/mongoose-paginate-v2": "^1.3.11", + "@icgc-argo/ego-token-utils": "^8.2.0", "adm-zip": "^0.4.16", "async": "^3.0.1", "bcrypt-nodejs": "^0.0.3", diff --git a/src/app.ts b/src/app.ts index c9069127e..ae3716d0e 100644 --- a/src/app.ts +++ b/src/app.ts @@ -37,6 +37,7 @@ import icgcImport from './routes/icgc-import'; import exceptionRouter from './routes/exception'; import responseTime from 'response-time'; import morgan from 'morgan'; +import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; const L = loggerFor(__filename); @@ -49,6 +50,7 @@ export type GlobalGqlContext = { isUserRequest: boolean; egoToken: string; Authorization: string; + userJwtData: EgoJwtData | undefined; dataLoaders: {}; }; diff --git a/src/server.ts b/src/server.ts index 67bb774b1..ac0d2c98e 100644 --- a/src/server.ts +++ b/src/server.ts @@ -40,7 +40,7 @@ import { startStandaloneServer, } from '@apollo/server/standalone'; import schema from './schemas/index'; -// import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; +import { EgoJwtData } from '@icgc-argo/ego-token-utils/dist/common'; let secrets: any = {}; let server: Server; @@ -167,7 +167,7 @@ let server: Server; > = async ({ req, res }) => { // Get the user token from the headers. const authHeader = req.headers.authorization; - let userJwtData = undefined; + let userJwtData: EgoJwtData | undefined = undefined; try { if (authHeader) { const jwt = authHeader.replace('Bearer ', ''); @@ -180,6 +180,7 @@ let server: Server; isUserRequest: true, egoToken: (authHeader || '').split('Bearer ').join(''), Authorization: `Bearer ${(authHeader || '').replace(/^Bearer[\s]*!/, '')}` || '', + userJwtData, dataLoaders: {}, }; };