From 0d70f27d4f0ab7a52f15c12dc38445fd10a0dd21 Mon Sep 17 00:00:00 2001 From: Abel Mathew Date: Mon, 15 Nov 2021 21:34:15 +0530 Subject: [PATCH] Pre-requisite for setup datasources (#83) * Remove unused files * delete deepsource integration * Add [src] to watch for restart on PM2 * Parse JWT for createdBy and UpdatedBy * use updated cBy and uBY for sign mutation * Use Transactions for saving sign for users * Setup Flat Types for nested types * Set sanitizeFilter: true * fetch user by JWT --- .deepsource.toml | 13 ---- ecosystem.config.js | 2 +- src/graphql/mutation/sign.js | 47 ++++++++------ src/graphql/mutation/user.js | 11 ++-- src/graphql/mutation/welcome.js | 54 ----------------- src/graphql/query/Welcome.js | 25 -------- src/graphql/query/sign.js | 24 +++----- src/graphql/query/user.js | 11 +++- src/graphql/resolver/addUser.js | 30 --------- src/graphql/resolver/index.js | 2 - src/graphql/resolver/updateUser.js | 26 -------- src/graphql/types/CertificateType.js | 60 ------------------ src/graphql/types/RequestType.js | 47 -------------- src/graphql/types/Welcome.js | 11 ---- src/graphql/types/common.js | 91 ---------------------------- src/graphql/types/flats.js | 27 +++++++++ src/graphql/types/index.js | 8 +-- src/graphql/types/sign.js | 10 ++- src/graphql/types/user.js | 10 ++- src/index.js | 62 +++---------------- src/models/certificate.js | 12 ++-- src/models/request.js | 10 +-- src/models/sign.js | 20 ++---- src/models/user.js | 10 +-- src/models/welcome.js | 16 ----- src/utils/index.js | 66 ++++++++++++++++++-- 26 files changed, 183 insertions(+), 522 deletions(-) delete mode 100644 .deepsource.toml delete mode 100644 src/graphql/mutation/welcome.js delete mode 100644 src/graphql/query/Welcome.js delete mode 100644 src/graphql/resolver/addUser.js delete mode 100644 src/graphql/resolver/index.js delete mode 100644 src/graphql/resolver/updateUser.js delete mode 100644 src/graphql/types/CertificateType.js delete mode 100644 src/graphql/types/RequestType.js delete mode 100644 src/graphql/types/Welcome.js delete mode 100644 src/graphql/types/common.js create mode 100644 src/graphql/types/flats.js delete mode 100644 src/models/welcome.js diff --git a/.deepsource.toml b/.deepsource.toml deleted file mode 100644 index 5461a69..0000000 --- a/.deepsource.toml +++ /dev/null @@ -1,13 +0,0 @@ -version = 1 - -[[analyzers]] -name = "test-coverage" -enabled = true - -[[analyzers]] -name = "secrets" -enabled = true - -[[analyzers]] -name = "javascript" -enabled = true \ No newline at end of file diff --git a/ecosystem.config.js b/ecosystem.config.js index 836e56f..d6fd04b 100644 --- a/ecosystem.config.js +++ b/ecosystem.config.js @@ -5,7 +5,7 @@ module.exports = { { name: 'dev', script: './src/index.js', - watch: '.', + watch: ['src'], env: { NODE_ENV: 'development', }, diff --git a/src/graphql/mutation/sign.js b/src/graphql/mutation/sign.js index 2ec53de..363e05e 100644 --- a/src/graphql/mutation/sign.js +++ b/src/graphql/mutation/sign.js @@ -1,41 +1,48 @@ -const { GraphQLString, GraphQLNonNull, GraphQLID } = require('graphql'); +const { GraphQLString, GraphQLNonNull } = require('graphql'); +const mongoose = require('mongoose'); // Type Defs -const SignType = require('../types/sign'); +const { SignType } = require('../types/'); const UserModel = require('../../models/user'); const SignModel = require('../../models/sign'); -const { addCreatedAndUpdatedBy } = require('../../utils/index'); const { GraphQLError } = require('graphql'); const createSign = { + name: 'createSign', type: SignType, args: { - userID: { type: GraphQLID }, - userMail: { type: GraphQLString }, name: { type: GraphQLNonNull(GraphQLString) }, image: { type: GraphQLNonNull(GraphQLString) }, designation: { type: GraphQLNonNull(GraphQLString) }, }, - async resolve(_, { userID, userMail, name, image, designation }) { - if (userID) { - const ifUserExists = await UserModel.exists({ _id: userID }).exec(); - if (ifUserExists) { - const sign = new SignModel({ userID, userMail, name, image, designation, ...addCreatedAndUpdatedBy(null) }); - return sign.save(); - } - return new GraphQLError('User Not in Database'); + async resolve(_, { name, image, designation }, { decodedToken, addCreatedAndUpdatedByWithUser }) { + if (!decodedToken || !decodedToken.sub) { + return new GraphQLError('Missing fields in the Auth Token'); } - if (userMail) { - const userID = await UserModel.findOne({ mail: userMail }, '_id').exec(); - if (userID) { - const sign = new SignModel({ userID, userMail, name, image, designation, ...addCreatedAndUpdatedBy(null) }); - return sign.save(); - } + const { sub } = decodedToken; + const userFromDB = await UserModel.findOne({ authProviderID: sub }).setOptions({ sanitizeFilter: true }).exec(); + if (!userFromDB._id) { return new GraphQLError('User Not in Database'); } - return new GraphQLError('Missing fields'); + const session = await mongoose.startSession(); + let response; + await session.withTransaction(async () => { + const signToSave = new SignModel({ + userID: userFromDB._id, + name, + image, + designation, + ...addCreatedAndUpdatedByWithUser(), + }); + const signWithID = await signToSave.save(); + userFromDB.signs.push(signWithID._id); + await userFromDB.save(); + response = signWithID; + }); + session.endSession(); + return response; }, }; diff --git a/src/graphql/mutation/user.js b/src/graphql/mutation/user.js index ff30962..fa0f2f7 100644 --- a/src/graphql/mutation/user.js +++ b/src/graphql/mutation/user.js @@ -1,20 +1,19 @@ const { GraphQLString, GraphQLNonNull, GraphQLError } = require('graphql'); // Type Defs -const UserType = require('../types/user'); +const { UserType } = require('../types'); const User = require('../../models/user'); -const { addCreatedAndUpdatedBy } = require('../../utils/index'); - const createUser = { + name: 'createUser', type: UserType, args: { name: { type: GraphQLNonNull(GraphQLString) }, displayPicture: { type: GraphQLNonNull(GraphQLString) }, }, - resolve(_, { name, displayPicture }, { decodedToken }) { - if (!decodedToken) { + resolve(_, { name, displayPicture }, { decodedToken, addCreatedAndUpdatedByWithUser }) { + if (!decodedToken || !decodedToken.email || !decodedToken.sub) { return new GraphQLError('Missing fields in the Auth Token'); } const { email: mail, sub: authProviderID } = decodedToken; @@ -23,7 +22,7 @@ const createUser = { name, displayPicture, authProviderID, - ...addCreatedAndUpdatedBy(null), + ...addCreatedAndUpdatedByWithUser(), }); return user.save(); }, diff --git a/src/graphql/mutation/welcome.js b/src/graphql/mutation/welcome.js deleted file mode 100644 index 56298ad..0000000 --- a/src/graphql/mutation/welcome.js +++ /dev/null @@ -1,54 +0,0 @@ -const { GraphQLInt, GraphQLNonNull, GraphQLString } = require('graphql'); - -const { WelcomeType } = require('../types/Welcome'); - -// Models -const WelcomeModel = require('../../models/welcome.js'); - -const addNewWelcomeMessage = { - type: WelcomeType, - args: { - message: { type: new GraphQLNonNull(GraphQLString) }, - status: { type: new GraphQLNonNull(GraphQLInt) }, - }, - async resolve(parent, { message, status }) { - const checkWelcome = await WelcomeModel.findOne({ status }); - - if (checkWelcome) { - throw new Error(`Message with status ${status} already exists.`); - } - - const welcome = new WelcomeModel({ - message, - status, - }); - - try { - return await welcome.save(); - } catch (error) { - throw new Error('Could not save the welcome message.', error); - } - }, -}; - -const deleteWelcomeMessage = { - type: WelcomeType, - args: { - status: { type: new GraphQLNonNull(GraphQLInt) }, - }, - async resolve(parent, { status }) { - const checkWelcome = await WelcomeModel.findOne({ status }); - - if (!checkWelcome) { - throw new Error(`No Message with status ${status} found`); - } - - try { - return await WelcomeModel.findOneAndDelete({ status }); - } catch (error) { - throw new Error('Could not delete the welcome message.', error); - } - }, -}; - -module.exports = { deleteWelcomeMessage, addNewWelcomeMessage }; diff --git a/src/graphql/query/Welcome.js b/src/graphql/query/Welcome.js deleted file mode 100644 index eaef5d7..0000000 --- a/src/graphql/query/Welcome.js +++ /dev/null @@ -1,25 +0,0 @@ -const { GraphQLInt, GraphQLNonNull, GraphQLList } = require('graphql'); - -// Type Defs -const WelcomeType = require('../types/welcome.js'); - -// Models -const WelcomeModel = require('../../models/welcome.js'); - -const getWelcomeMessages = { - type: new GraphQLList(WelcomeType), - args: {}, - resolve() { - return WelcomeModel.find({}); - }, -}; - -const getWelcomeMessage = { - type: WelcomeType, - args: { status: { type: new GraphQLNonNull(GraphQLInt) } }, - resolve(parent, args) { - return WelcomeModel.findOne({ status: args.status }); - }, -}; - -module.exports = { getWelcomeMessage, getWelcomeMessages }; diff --git a/src/graphql/query/sign.js b/src/graphql/query/sign.js index 17582e8..99aa4da 100644 --- a/src/graphql/query/sign.js +++ b/src/graphql/query/sign.js @@ -1,27 +1,17 @@ -const { GraphQLError, GraphQLID, GraphQLList } = require('graphql'); +const { GraphQLID, GraphQLNonNull } = require('graphql'); // Type Defs -const SignType = require('../types/sign'); +const { SignType } = require('../types/'); // Models const SignModel = require('../../models/sign'); -const UserModel = require('../../models/user'); const getSign = { - type: GraphQLList(SignType), + name: 'getSign', + type: SignType, args: { - signId: { type: GraphQLID }, - userId: { type: GraphQLID }, + id: { type: GraphQLNonNull(GraphQLID) }, }, - async resolve(_, { signId, userId }) { - if (signId) { - return [SignModel.findById(signId).exec()]; - } - if (userId) { - return ( - (await UserModel.findById(userId).select('signs').populate({ path: 'signs', model: 'sign' }).exec())?.signs || - [] - ); - } - return new GraphQLError('signId or userId is required'); + resolve(_, { id }) { + return SignModel.findById(id).exec(); }, }; diff --git a/src/graphql/query/user.js b/src/graphql/query/user.js index 97e863b..f4a535b 100644 --- a/src/graphql/query/user.js +++ b/src/graphql/query/user.js @@ -1,22 +1,27 @@ const { GraphQLError, GraphQLID, GraphQLString } = require('graphql'); // Type Defs -const UserType = require('../types/user'); +const { UserType } = require('../types/'); // Models const UserModel = require('../../models/user'); const getUser = { + name: 'getUser', type: UserType, args: { id: { type: GraphQLID }, mail: { type: GraphQLString }, }, - resolve(_, { id, mail }) { + resolve(_, { id, mail }, { decodedToken }) { + const { sub } = decodedToken || {}; + if (sub) { + return UserModel.findOne({ authProviderID: sub }).setOptions({ sanitizeFilter: true }).exec(); + } if (id) { return UserModel.findById(id).exec(); } if (mail) { - return UserModel.findOne({ mail }).exec(); + return UserModel.findOne({ mail }).setOptions({ sanitizeFilter: true }).exec(); } return new GraphQLError('Missing fields'); }, diff --git a/src/graphql/resolver/addUser.js b/src/graphql/resolver/addUser.js deleted file mode 100644 index 9697045..0000000 --- a/src/graphql/resolver/addUser.js +++ /dev/null @@ -1,30 +0,0 @@ -import logger from '../../config/winston.js'; - -export default async (parent, args) => { - const { mail, name, displayPicture, blurHash, firebaseID, accessLevel } = args; - const existingUser = await User.findOne({ mail }); - - if (existingUser) { - throw new Error(`User with mail ${mail} already exists`); - } - - // TODO: add firebaseID and accessLevel to the check below - if (!mail || !name || !displayPicture || !blurHash) { - throw new Error(`Please add all fields`); - } - - const user = new User({ - mail, - name, - displayPicture, - blurHash, - firebaseID, - accessLevel, - }); - - try { - return await user.save(); - } catch (error) { - logger.error('Something went wrong while creating a user', error); - } -}; diff --git a/src/graphql/resolver/index.js b/src/graphql/resolver/index.js deleted file mode 100644 index 6d80f7e..0000000 --- a/src/graphql/resolver/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as addUser } from './addUser.js'; -export { default as updateUser } from './updateUser.js'; diff --git a/src/graphql/resolver/updateUser.js b/src/graphql/resolver/updateUser.js deleted file mode 100644 index b9a1c46..0000000 --- a/src/graphql/resolver/updateUser.js +++ /dev/null @@ -1,26 +0,0 @@ -import User from '../../models/user.js'; - -import logger from '../../config/winston.js'; - -export default async (parent, args) => { - const { id, name, displayPicture, blurHash } = args; - const existingUser = await User.findById(id); - - if (!existingUser) { - throw new Error(`User not found`); - } - - User.findByIdAndUpdate(id, { - $set: { - name, - displayPicture, - blurHash, - }, - }); - - try { - return await user.save(); - } catch (error) { - logger.error('Something went wrong while creating a user', error); - } -}; diff --git a/src/graphql/types/CertificateType.js b/src/graphql/types/CertificateType.js deleted file mode 100644 index ecd232b..0000000 --- a/src/graphql/types/CertificateType.js +++ /dev/null @@ -1,60 +0,0 @@ -import { GraphQLString, GraphQLObjectType, GraphQLID, GraphQLList } from 'graphql'; -import graphqlIsoDatefrom from 'graphql-iso-date'; - -// Models -import Request from '../../models/request.js'; - -// Types -import { PixelMap, CreatedByDetails } from './common.js'; -import RequestType from './RequestType'; - -const { GraphQLDateTime } = graphqlIsoDatefrom; - -export default new GraphQLObjectType({ - name: 'SignType', - fields: () => ({ - _id: { type: GraphQLID }, - request: { - type: RequestType, - async resolve(parent) { - const request = await Request.findById(parent.request); - if (!request) { - throw new Error('Request not found'); - } - return request; - }, - }, - title: { type: GraphQLString }, - description: { type: GraphQLString }, - template: { - type: new GraphQLObjectType({ - name: 'CertificateTemplate', - fields: () => ({ - src: { type: GraphQLString }, - blurHash: { type: GraphQLString }, - }), - }), - }, - font: { type: GraphQLString }, - mail: { type: GraphQLString }, - pixelMap: { type: PixelMap('CertificatePixelMap') }, - signMap: { - type: new GraphQLList({ - id: { type: GraphQLID }, - pixel: { - type: new GraphQLObjectType({ - name: 'ApproversPixelMap', - fields: () => ({ - x: { type: GraphQLInt }, - y: { type: GraphQLInt }, - }), - }), - }, - }), - }, - createdBy: CreatedByDetails, - updatedBy: CreatedByDetails, - createdAt: { type: GraphQLDateTime }, - updatedAt: { type: GraphQLDateTime }, - }), -}); diff --git a/src/graphql/types/RequestType.js b/src/graphql/types/RequestType.js deleted file mode 100644 index 3444210..0000000 --- a/src/graphql/types/RequestType.js +++ /dev/null @@ -1,47 +0,0 @@ -import { GraphQLString, GraphQLObjectType, GraphQLList, GraphQLEnumType } from 'graphql'; -import graphqlIsoDatefrom from 'graphql-iso-date'; - -// Types -import { CertificateInfo, Approver, PixelMap, CreatedByDetails } from './common.js'; -import UserType from './UserType.js'; - -const { GraphQLDateTime } = graphqlIsoDatefrom; - -export default new GraphQLObjectType({ - name: 'RequestType', - description: 'Request object created by the certificate initiator for approval', - fields: () => ({ - initiator: { - type: UserType, - async resolve(parent) { - const user = await User.findById(parent.initiator); - if (!user) { - throw new Error('Initiator not found.'); - } - return user; - }, - }, - title: { type: GraphQLString }, - description: { type: GraphQLString }, - approvers: { type: GraphQLList(Approver('RequestApprover')) }, - certificateInfo: { type: CertificateInfo('RequesetCertificateInfo') }, - pixelMap: { type: PixelMap('RequestPixelMap') }, - font: { type: GraphQLString }, - createdBy: CreatedByDetails, - updatedBy: CreatedByDetails, - createdAt: { type: GraphQLDateTime }, - updatedAt: { type: GraphQLDateTime }, - status: { - type: new GraphQLEnumType({ - name: 'status', - values: { - UnInitiated: { value: 'Un-Initiated' }, - Initiated: { value: 'Initiated' }, - InProcess: { value: 'In-Process' }, - Approved: { value: 'Approved' }, - Generated: { value: 'Generated' }, - }, - }), - }, - }), -}); diff --git a/src/graphql/types/Welcome.js b/src/graphql/types/Welcome.js deleted file mode 100644 index c0a985e..0000000 --- a/src/graphql/types/Welcome.js +++ /dev/null @@ -1,11 +0,0 @@ -const { GraphQLString, GraphQLInt, GraphQLObjectType } = require('graphql'); - -const WelcomeType = new GraphQLObjectType({ - name: 'Welcome', - fields: () => ({ - status: { type: GraphQLInt }, - message: { type: GraphQLString }, - }), -}); - -module.exports = WelcomeType; diff --git a/src/graphql/types/common.js b/src/graphql/types/common.js deleted file mode 100644 index 6dea448..0000000 --- a/src/graphql/types/common.js +++ /dev/null @@ -1,91 +0,0 @@ -import { GraphQLString, GraphQLInt, GraphQLObjectType, GraphQLID, GraphQLEnumType } from 'graphql'; -import graphqlIsoDatefrom from 'graphql-iso-date'; -const { GraphQLDateTime } = graphqlIsoDatefrom; - -import { User } from '../../models/index.js'; -import UserType from './UserType.js'; - -export const Approver = (name) => - new GraphQLObjectType({ - name, - fields: () => ({ - user: { - type: GraphQLID, - async resolve(parent) { - const user = await User.findById(parent.user); - if (!user) { - throw new Error('Initiator not found.'); - } - return user; - }, - }, - status: { - type: new GraphQLEnumType({ - name: 'ApprovalStatus', - values: { - Rejected: { value: 'Rejected' }, - Approved: { value: 'Approved' }, - Pending: { value: 'Pending' }, - }, - }), - }, - pixel: { - type: new GraphQLObjectType({ - name: `${name}ApproversPixelMap`, - fields: () => ({ - x: { type: GraphQLInt }, - y: { type: GraphQLInt }, - }), - }), - }, - scale: { type: GraphQLInt }, - approvedAt: { type: GraphQLDateTime }, - }), - }); - -export const CertificateInfo = (name) => - new GraphQLObjectType({ - name, - fields: () => ({ - template: { - type: new GraphQLObjectType({ - name: `${name}CertificateTemplate`, - fields: () => ({ - src: { type: GraphQLString }, - blurHash: { type: GraphQLString }, - }), - }), - }, - data: { type: GraphQLString }, - }), - }); - -export const PixelMap = (name) => - new GraphQLObjectType({ - name, - fields: () => ({ - columnName: { type: GraphQLString }, - pixel: { - type: new GraphQLObjectType({ - name: `${name}ApproversPixelMap`, - fields: () => ({ - x: { type: GraphQLInt }, - y: { type: GraphQLInt }, - }), - }), - }, - fontSize: { type: GraphQLInt }, - fontWeight: { type: GraphQLInt }, - }), - }); - -export const CreatedByDetails = { - type: UserType, - async resolve(parent) { - const user = await User.findById(parent.createdById); - if (!user) { - throw new Error('Initiator not found.'); - } - return user; - }, -}; diff --git a/src/graphql/types/flats.js b/src/graphql/types/flats.js new file mode 100644 index 0000000..4cb2ea5 --- /dev/null +++ b/src/graphql/types/flats.js @@ -0,0 +1,27 @@ +const { GraphQLString, GraphQLObjectType, GraphQLID } = require('graphql'); + +const SignFlatType = new GraphQLObjectType({ + name: 'SignFlatType', + fields: () => ({ + id: { type: GraphQLID }, + name: { type: GraphQLString }, + designation: { type: GraphQLString }, + image: { type: GraphQLString }, + }), +}); + +const UserFlatType = new GraphQLObjectType({ + name: 'UserFlatType', + fields: () => ({ + id: { type: GraphQLID }, + mail: { type: GraphQLString }, + name: { type: GraphQLString }, + displayPicture: { type: GraphQLString }, + authProviderID: { type: GraphQLString }, + }), +}); + +module.exports = { + SignFlatType, + UserFlatType, +}; diff --git a/src/graphql/types/index.js b/src/graphql/types/index.js index 0af51a0..552204b 100644 --- a/src/graphql/types/index.js +++ b/src/graphql/types/index.js @@ -1,7 +1,7 @@ -const UserType = require('./user'); -const SignType = require('./sign'); +const { UserType } = require('./user'); +const { SignType } = require('./sign'); module.exports = { - UserType: new UserType(), - SignType: new SignType(), + UserType, + SignType, }; diff --git a/src/graphql/types/sign.js b/src/graphql/types/sign.js index 7e63758..32f825c 100644 --- a/src/graphql/types/sign.js +++ b/src/graphql/types/sign.js @@ -1,14 +1,20 @@ const { GraphQLString, GraphQLObjectType, GraphQLID } = require('graphql'); +const { UserFlatType } = require('./flats'); + +const UserModel = require('../../models/user'); const SignType = new GraphQLObjectType({ name: 'SignType', fields: () => ({ id: { type: GraphQLID }, - userID: { type: GraphQLID }, name: { type: GraphQLString }, designation: { type: GraphQLString }, image: { type: GraphQLString }, + user: { + type: UserFlatType, + resolve: (parent) => UserModel.findById(parent.userID).exec(), + }, }), }); -module.exports = SignType; +module.exports = { SignType }; diff --git a/src/graphql/types/user.js b/src/graphql/types/user.js index 8f0bf68..aa6b195 100644 --- a/src/graphql/types/user.js +++ b/src/graphql/types/user.js @@ -1,5 +1,5 @@ const { GraphQLString, GraphQLObjectType, GraphQLID, GraphQLList } = require('graphql'); -const SignType = require('./sign'); +const { SignFlatType } = require('./flats'); const SignModel = require('../../models/sign'); const UserType = new GraphQLObjectType({ @@ -11,12 +11,10 @@ const UserType = new GraphQLObjectType({ displayPicture: { type: GraphQLString }, authProviderID: { type: GraphQLString }, signs: { - type: new GraphQLList(SignType), - resolve(parent) { - return SignModel.find({ _id: parent.signs }).exec(); - }, + type: new GraphQLList(SignFlatType), + resolve: (parent) => SignModel.find({ _id: parent.signs }).setOptions({ sanitizeFilter: true }).exec(), }, }), }); -module.exports = UserType; +module.exports = { UserType }; diff --git a/src/index.js b/src/index.js index 003376e..83326ed 100644 --- a/src/index.js +++ b/src/index.js @@ -1,11 +1,6 @@ -//TODO: Check if pm2-runtme works // Libraries const express = require('express'); -const jwt = require('jsonwebtoken'); -const jwksClient = require('jwks-rsa'); const { ApolloServer } = require('apollo-server-express'); -const fetch = require('node-fetch'); - const cors = require('cors'); // GraphQL Schema @@ -13,6 +8,7 @@ const schema = require('./graphql/index.js'); // Utilities const logger = require('./config/winston.js'); +const { decodeTokenFromHeader, addCreatedAndUpdatedBy, addUpdatedBy } = require('./utils/index.js'); // Initialize Firebase, Mongoose, Cloudinary Admin SDK require('./config/mongoose.js'); @@ -22,19 +18,6 @@ require('./config/cloudinary.js'); // Create Express app instance const app = express(); -const AUTH0_DOMAIN = 'signit.eu.auth0.com'; - -const authClient = jwksClient({ - jwksUri: `https://${AUTH0_DOMAIN}/.well-known/jwks.json`, -}); - -const getKey = (header, callback) => { - authClient.getSigningKey(header.kid, (err, key) => { - const signingKey = key.getPublicKey(); - callback(null, signingKey); - }); -}; - // Setup Cross-Origin Resource Sharing for the development environment // localhost:3000 would be the frontend port on which the app is running const whitelist = { @@ -56,7 +39,6 @@ const corsOptions = { // Middlewares // JSON and Encoded URL Body Parser, Use Cors -app.use(express.json()); app.use(cors(corsOptions)); async function startServer() { @@ -67,43 +49,13 @@ async function startServer() { playground: process.env.NODE_ENV !== 'production', debug: process.env.NODE_ENV !== 'production', context: async ({ req }) => { - const authHeader = req.headers.authorization; - const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null; - const decodedToken = await new Promise((resolve, reject) => { - jwt.verify( - token, - getKey, - { - algorithms: ['RS256'], - audience: [`https://${AUTH0_DOMAIN}/userinfo`], - issuer: `https://${AUTH0_DOMAIN}/`, - }, - (err, decoded) => { - if (err) { - reject(err); - } else { - resolve(decoded); - } - } - ); - }).catch((err) => { - logger.error(err); - return null; - }); - if (!decodedToken) { - return { decodedToken }; - } - const userDetailsByIdUrl = `https://${AUTH0_DOMAIN}/api/v2/users/${decodedToken.sub}`; - const userDetails = await fetch(userDetailsByIdUrl, { - headers: { - Authorization: `Bearer ${token}`, - }, - }) - .then((res) => res.json()) - .catch(() => null); - + const decodedToken = await decodeTokenFromHeader(req.headers.authorization); + const addCreatedAndUpdatedByWithUser = addCreatedAndUpdatedBy.bind(null, decodedToken); + const addUpdatedByWithUser = addUpdatedBy.bind(null, decodedToken); return { - decodedToken: { email: userDetails?.email, email_verified: userDetails?.email_verified, ...decodedToken }, + decodedToken, + addCreatedAndUpdatedByWithUser, + addUpdatedByWithUser, }; }, }); diff --git a/src/models/certificate.js b/src/models/certificate.js index 8c86ff0..381259c 100644 --- a/src/models/certificate.js +++ b/src/models/certificate.js @@ -91,14 +91,14 @@ const certificateSchema = new Schema( }, ], createdBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later - trim: true, + type: String, + required: true, + default: 'system', }, updatedBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later - trim: true, + type: String, + required: true, + default: 'system', }, schemaVersion: { type: Number, diff --git a/src/models/request.js b/src/models/request.js index f9395f4..824119a 100644 --- a/src/models/request.js +++ b/src/models/request.js @@ -103,12 +103,14 @@ const requestSchema = new Schema( default: 'Arial', }, createdBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later + type: String, + required: true, + default: 'system', }, updatedBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later + type: String, + required: true, + default: 'system', }, schemaVersion: { type: Number, diff --git a/src/models/sign.js b/src/models/sign.js index f3f4e86..d3d235e 100644 --- a/src/models/sign.js +++ b/src/models/sign.js @@ -1,14 +1,10 @@ const { Schema, model } = require('mongoose'); -const UserModel = require('./user'); - const signSchema = new Schema( { userID: { type: Schema.ObjectId, required: true, - unique: true, - index: true, }, name: { type: String, @@ -37,14 +33,14 @@ const signSchema = new Schema( trim: true, }, createdBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later - trim: true, + type: String, + required: true, + default: 'system', }, updatedBy: { - type: Schema.ObjectId, - required: false, // TODO: change to true later - trim: true, + type: String, + required: true, + default: 'system', }, schemaVersion: { type: Number, @@ -55,8 +51,4 @@ const signSchema = new Schema( { timestamps: true } ); -signSchema.post('save', async (sign) => { - await UserModel.findOneAndUpdate({ _id: sign.userID }, { $push: { signs: sign._id } }, { new: true }); -}); - module.exports = model('sign', signSchema); diff --git a/src/models/user.js b/src/models/user.js index d714c17..13fdb91 100644 --- a/src/models/user.js +++ b/src/models/user.js @@ -31,12 +31,14 @@ const userSchema = new Schema( }, ], createdBy: { - type: Schema.ObjectId, - required: false, // TODO: Change to true later + type: String, + required: true, + default: 'system', }, updatedBy: { - type: Schema.ObjectId, - required: false, // TODO: Change to true later + type: String, + required: true, + default: 'system', }, schemaVersion: { type: Number, diff --git a/src/models/welcome.js b/src/models/welcome.js deleted file mode 100644 index 19f4917..0000000 --- a/src/models/welcome.js +++ /dev/null @@ -1,16 +0,0 @@ -const { Schema, model } = require('mongoose'); - -const welcomeSchema = new Schema({ - message: { - type: String, - required: true, - default: 'Apollo-Server is working!', - }, - status: { - type: Number, - required: true, - default: 200, - }, -}); - -module.exports = model('welcome', welcomeSchema); diff --git a/src/utils/index.js b/src/utils/index.js index 15595c5..d676c29 100644 --- a/src/utils/index.js +++ b/src/utils/index.js @@ -1,11 +1,67 @@ -const addCreatedAndUpdatedBy = (user) => { - const userID = user; //TODO: Parse JWT/equivalent here +const jwt = require('jsonwebtoken'); +const jwksClient = require('jwks-rsa'); +const fetch = require('node-fetch'); + +const AUTH0_DOMAIN = 'signit.eu.auth0.com'; + +const addCreatedAndUpdatedBy = (decodedToken) => { + const userID = decodedToken?.sub; return { updatedBy: userID, createdBy: userID }; }; -const addUpdatedBy = (user) => { - const userID = user; //TODO: Parse JWT/equivalent here +const addUpdatedBy = (decodedToken) => { + const userID = decodedToken?.sub; return { updatedBy: userID }; }; -module.exports = { addCreatedAndUpdatedBy, addUpdatedBy }; +const authClient = jwksClient({ + jwksUri: `https://${AUTH0_DOMAIN}/.well-known/jwks.json`, +}); + +const getKey = (header, callback) => { + authClient.getSigningKey(header.kid, (err, key) => { + const signingKey = key.getPublicKey(); + callback(null, signingKey); + }); +}; + +const decodeTokenFromHeader = async (authHeader) => { + const token = authHeader?.startsWith('Bearer ') ? authHeader.substring(7) : null; + const decodedToken = await new Promise((resolve, reject) => { + jwt.verify( + token, + getKey, + { + algorithms: ['RS256'], + audience: [`https://${AUTH0_DOMAIN}/userinfo`], + issuer: `https://${AUTH0_DOMAIN}/`, + }, + (err, decoded) => { + if (err) { + reject(err); + } else { + resolve(decoded); + } + } + ); + }).catch(() => null); + if (!decodedToken) { + return { decodedToken }; + } + const userDetailsByIdUrl = `https://${AUTH0_DOMAIN}/api/v2/users/${decodedToken.sub}`; + const userDetails = await fetch(userDetailsByIdUrl, { + headers: { + Authorization: `Bearer ${token}`, + }, + }) + .then((res) => res.json()) + .catch(() => null); + + return { + email: userDetails?.email, + email_verified: userDetails?.email_verified, + ...decodedToken, + }; +}; + +module.exports = { addCreatedAndUpdatedBy, addUpdatedBy, decodeTokenFromHeader };