// session.js // Copyright (C) 2022,2023 DTP Technologies, LLC // All Rights Reserved 'use strict'; import util from 'node:util'; import passport from 'passport'; import { SiteService, SiteError } from '../../lib/site-lib.js'; export default class SessionService extends SiteService { static get slug () { return 'session'; } static get name ( ) { return 'SessionService'; } constructor (dtp) { super(dtp, SessionService); } async start ( ) { await super.start(); passport.serializeUser(this.serializeUser.bind(this)); passport.deserializeUser(this.deserializeUser.bind(this)); } async stop ( ) { this.log.info(`stopping ${SessionService.name} service`); } middleware ( ) { return async (req, res, next) => { res.locals.user = req.user; res.locals.query = req.query; if (req.user) { if (req.user.flags.isAdmin) { res.locals.config = this.dtp.config; res.locals.session = req.session; res.locals.util = util; } } return next(); }; } authCheckMiddleware (options) { const { membership: membershipService } = this.dtp.services; options = Object.assign({ requireLogin: true, requireEmailVerified: false, requireMembership: false, requireModerator: false, requireAdmin: false, }, options); return async (req, res, next) => { if (options.requireLogin && !req.user) { return next(new SiteError(403, 'Login required')); } if (options.requireEmailVerified && (!req.user || !req.user.flags.isEmailVerified)) { return next(new SiteError(403, `Must verify your email address to continue. Please check your spam folder for a welcome email from ${this.dtp.config.site.name}`)); } if (options.requireMembership) { res.locals.membership = await membershipService.getForUser(req.user); if (!res.locals.membership) { return next(new SiteError(403, 'Membership required')); } } if (options.requireModerator && (!req.user || !req.user.flags.isModerator)) { return next(new SiteError(403, 'Platform moderator privileges are required')); } if (options.requireAdmin && (!req.user || !req.user.flags.isAdmin)) { return next(new SiteError(403, 'Platform administrator privileges are required')); } return next(); }; } async serializeUser (user, done) { return done(null, user._id); } async deserializeUser (userId, done) { const { user: userService } = this.dtp.services; try { const user = await userService.getUserAccount(userId); if (user.permissions && !user.permissions.canLogin) { return done(null, null); // destroys user session without error } return done(null, user); } catch (error) { this.log.error('failed to deserialize user from session', { error }); return done(null, null); } } }