Asymmetrik/node-rest-starter

View on GitHub
src/app/core/user/user-auth.middleware.ts

Summary

Maintainability
A
25 mins
Test Coverage
import _ from 'lodash';

import userAuthService from './auth/user-authentication.service';
import userAuthorizationService from './auth/user-authorization.service';
import { requiresEua } from './eua/eua.controller';
import { config } from '../../../dependencies';
import { ForbiddenError, UnauthorizedError } from '../../common/errors';
import { has, hasAll, requiresAny } from '../../common/express/auth-middleware';

/**
 * Checks that the user is logged in
 *     1. The user is logged in
 */
export const hasLogin = (req, res, next) => {
    has(requiresLogin)(req, res, next);
};

/**
 * Checks that the user has base access
 *     1. The user is logged in
 *     2. The user has accepted the EUA if applicable
 *     3. The user has the 'user' role
 */
export const hasAccess = (req, res, next) => {
    hasAll(
        requiresLogin,
        requiresOrganizationLevels,
        requiresAny([requiresUserRole, requiresMachineRole]),
        requiresExternalRoles,
        requiresEua
    )(req, res, next);
};

/**
 * Checks that the user has editor access
 *     1. The user has met the base access requirements
 *     2. The user has the 'editor' role
 */
export const hasEditorAccess = (req, res, next) => {
    hasAll(
        requiresLogin,
        requiresOrganizationLevels,
        requiresAny([requiresUserRole, requiresMachineRole]),
        requiresExternalRoles,
        requiresAny([requiresAdminRole, requiresEditorRole]),
        requiresEua
    )(req, res, next);
};

/**
 * Checks that the user has auditor access
 *     1. The user has met the base access requirements
 *     2. The user has the 'auditor' role
 */
export const hasAuditorAccess = (req, res, next) => {
    hasAll(
        requiresLogin,
        requiresOrganizationLevels,
        requiresUserRole,
        requiresExternalRoles,
        requiresAuditorRole,
        requiresEua
    )(req, res, next);
};

/**
 * Checks that the user has admin access
 *     1. The user has met the base access requirements
 *     2. The user has the 'admin' role
 */
export const hasAdminAccess = (req, res, next) => {
    hasAll(requiresLogin, requiresAdminRole)(req, res, next);
};

/**
 * Require an authenticated user
 */
export const requiresLogin = (req, res, next) => {
    if (req.isAuthenticated()) {
        return Promise.resolve();
    }

    // Only try to auto login if it's explicitly set in the config
    if (config.get<boolean>('auth.autoLogin')) {
        return userAuthService.authenticateAndLogin(req, res, next);
    }
    // Otherwise don't
    return Promise.reject(new UnauthorizedError('User is not logged in'));
};

/**
 * Require the passed roles
 */
export const requiresRoles = (
    roles: string[],
    errorMessage = 'User is missing required roles'
) => {
    return (req) => {
        if (userAuthorizationService.hasRoles(req.user, roles)) {
            return Promise.resolve();
        }
        return Promise.reject(new ForbiddenError(errorMessage));
    };
};

//Detects if the user has the user role
export const requiresUserRole = (req) => {
    return requiresRoles(['user'], 'User account is inactive')(req);
};

//Detects if the user has the editor role
export const requiresEditorRole = (req) => {
    return requiresRoles(['editor'])(req);
};

//Detects if the user has the auditor role
export const requiresAuditorRole = (req) => {
    return requiresRoles(['auditor'])(req);
};

// Detects if the user has admin role
export const requiresAdminRole = (req) => {
    return requiresRoles(['admin'])(req);
};

//Detects if the user has the machine role
export const requiresMachineRole = (req) => {
    return requiresRoles(['machine'])(req);
};

// Checks to see if all required external roles are accounted for
export const requiresExternalRoles = (req) => {
    const requiredRoles = config.get<string[]>('auth.requiredRoles');

    // If there are required roles, check for them
    if (req.user.bypassAccessCheck === false && requiredRoles.length > 0) {
        // Get the user roles
        const userRoles = _.isArray(req.user.externalRoles)
            ? req.user.externalRoles
            : [];

        // Reject if the user is missing required roles
        if (_.difference(requiredRoles, userRoles).length > 0) {
            return Promise.reject(
                new ForbiddenError('User is missing required external roles')
            );
        }
        // Resolve if they had all the roles
        return Promise.resolve();
    }
    // Resolve if we don't need to check
    return Promise.resolve();
};

/**
 * Checks whether user has defined organization level values if values are required
 */
export const requiresOrganizationLevels = (req) => {
    const required = config.get('auth.orgLevelConfig.required');

    if (!required) {
        // Organization levels are not required, proceed
        return Promise.resolve();
    }

    if (userAuthorizationService.hasRoles(req.user, ['admin'])) {
        // Admins can bypass this requirement
        return Promise.resolve();
    }

    return !_.isEmpty(req.user.organizationLevels)
        ? Promise.resolve()
        : Promise.reject(
                new ForbiddenError('User must select organization levels.')
          );
};