From cb2e1b3907b568b388d61b8a71710940d26db289 Mon Sep 17 00:00:00 2001 From: csrapr <26280794+csrapr@users.noreply.github.com> Date: Mon, 24 May 2021 21:57:39 +0100 Subject: [PATCH] Feat: adds async local storage to admin server --- lib/constants.js | 8 ++++++- lib/new-admin/admin-server.js | 6 +++++ .../graphql/modules/authentication.js | 5 ++-- .../middlewares/cleanUserSessions.js | 23 +++++++++++++++++++ lib/new-admin/middlewares/session.js | 13 +++++++---- 5 files changed, 47 insertions(+), 8 deletions(-) create mode 100644 lib/new-admin/middlewares/cleanUserSessions.js diff --git a/lib/constants.js b/lib/constants.js index aa7319fa..9e526ce6 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -1,3 +1,5 @@ +const T = require('./time') + const anonymousCustomer = { uuid: '47ac1184-8102-11e7-9079-8f13a7117867', name: 'anonymous' @@ -8,6 +10,8 @@ const cassetteMaxCapacity = 500 const AUTHENTICATOR_ISSUER_ENTITY = 'Lamassu' const AUTH_TOKEN_EXPIRATION_TIME = '30 minutes' const REGISTRATION_TOKEN_EXPIRATION_TIME = '30 minutes' +const USER_SESSIONS_TABLE_NAME = 'user_sessions' +const USER_SESSIONS_CLEAR_INTERVAL = 1 * T.hour const AUTOMATIC = 'automatic' const MANUAL = 'manual' @@ -19,5 +23,7 @@ module.exports = { AUTH_TOKEN_EXPIRATION_TIME, REGISTRATION_TOKEN_EXPIRATION_TIME, AUTOMATIC, - MANUAL + MANUAL, + USER_SESSIONS_TABLE_NAME, + USER_SESSIONS_CLEAR_INTERVAL } diff --git a/lib/new-admin/admin-server.js b/lib/new-admin/admin-server.js index b666333f..05d1e377 100644 --- a/lib/new-admin/admin-server.js +++ b/lib/new-admin/admin-server.js @@ -19,6 +19,9 @@ const logger = require('../logger') const session = require('./middlewares/session') const { AuthDirective } = require('./graphql/directives') const { typeDefs, resolvers } = require('./graphql/schema') +const computeSchema = require('../compute-schema') +const cleanUserSessions = require('./middlewares/cleanUserSessions') +const { USER_SESSIONS_CLEAR_INTERVAL } = require('../constants') const devMode = require('minimist')(process.argv.slice(2)).dev const idPhotoCardBasedir = _.get('idPhotoCardDir', options) @@ -32,6 +35,7 @@ if (!hostname) { } const app = express() + app.use(helmet()) app.use(compression()) app.use(nocache()) @@ -39,6 +43,8 @@ app.use(cookieParser()) app.use(express.json()) app.use(express.urlencoded({ extended: true })) // support encoded bodies app.use(express.static(path.resolve(__dirname, '..', '..', 'public'))) +app.use(computeSchema) +app.use(cleanUserSessions(USER_SESSIONS_CLEAR_INTERVAL)) app.use(session) const apolloServer = new ApolloServer({ diff --git a/lib/new-admin/graphql/modules/authentication.js b/lib/new-admin/graphql/modules/authentication.js index 798e6aa1..690bc907 100644 --- a/lib/new-admin/graphql/modules/authentication.js +++ b/lib/new-admin/graphql/modules/authentication.js @@ -30,8 +30,7 @@ const authenticateUser = (username, password) => { const destroySessionIfSameUser = (context, user) => { const sessionUser = getUserFromCookie(context) - if (sessionUser && user.id === sessionUser.id) - context.req.session.destroy() + if (sessionUser && user.id === sessionUser.id) { context.req.session.destroy() } } const destroySessionIfBeingUsed = (sessID, context) => { @@ -60,7 +59,7 @@ const executeProtectedAction = (code, id, context, action) => { if (user.role !== 'superuser') { return action() } - + return confirm2FA(code, context) .then(() => action()) }) diff --git a/lib/new-admin/middlewares/cleanUserSessions.js b/lib/new-admin/middlewares/cleanUserSessions.js new file mode 100644 index 00000000..c156c740 --- /dev/null +++ b/lib/new-admin/middlewares/cleanUserSessions.js @@ -0,0 +1,23 @@ +const { asyncLocalStorage } = require('../../async-storage') +const db = require('../../db') +const { USER_SESSIONS_TABLE_NAME } = require('../../constants') + +const schemaCache = {} + +const cleanUserSessions = (cleanInterval) => (req, res, next) => { + const schema = asyncLocalStorage.getStore() ? asyncLocalStorage.getStore().get('schema') : null + const now = Date.now() + + if (!schema) return next() + if (schema && schemaCache.schema + cleanInterval > now) return next() + + console.log('Clearing old sessions for schema', schema) + return db.$none('DELETE FROM $1^ WHERE expire < to_timestamp($2)', [USER_SESSIONS_TABLE_NAME, now]) + .then(() => { + schemaCache.schema = now + return next() + }) + .catch(next) +} + +module.exports = cleanUserSessions diff --git a/lib/new-admin/middlewares/session.js b/lib/new-admin/middlewares/session.js index edb323d8..dc0ba1fd 100644 --- a/lib/new-admin/middlewares/session.js +++ b/lib/new-admin/middlewares/session.js @@ -3,10 +3,11 @@ const express = require('express') const router = express.Router() const hkdf = require('futoin-hkdf') const session = require('express-session') -const pgSession = require('connect-pg-simple')(session) +const PgSession = require('connect-pg-simple')(session) const mnemonicHelpers = require('../../mnemonic-helpers') const db = require('../../db') const options = require('../../options') +const { USER_SESSIONS_TABLE_NAME } = require('../../constants') const getSecret = () => { const mnemonic = fs.readFileSync(options.mnemonicPath, 'utf8') @@ -19,10 +20,14 @@ const getSecret = () => { const hostname = options.hostname +const lamaDb = { + query: (query, values, qrm) => db.$query(query, values, qrm, false) +} + router.use('*', session({ - store: new pgSession({ - pgPromise: db, - tableName: 'user_sessions' + store: new PgSession({ + pgPromise: lamaDb, + tableName: USER_SESSIONS_TABLE_NAME }), name: 'lid', secret: getSecret(),