oaeproject/Hilary

View on GitHub
packages/oae-authentication/lib/strategies/signed/strategy.js

Summary

Maintainability
A
0 mins
Test Coverage
B
86%
/*!
 * Copyright 2014 Apereo Foundation (AF) Licensed under the
 * Educational Community License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may
 * obtain a copy of the License at
 *
 *     http://opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an "AS IS"
 * BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
 * or implied. See the License for the specific language governing
 * permissions and limitations under the License.
 */

import { inherits } from 'node:util';
import passport from 'passport';

import * as PrincipalsDAO from 'oae-principals/lib/internal/dao.js';

import { AuthenticationConstants } from 'oae-authentication/lib/constants.js';
import * as AuthenticationSignedUtil from 'oae-authentication/lib/strategies/signed/util.js';
import * as AuthenticationUtil from 'oae-authentication/lib/util.js';

const Strategy = function () {
  passport.Strategy.call(this);
  this.name = 'signed';
};

/**
 * Inherit from `passport.Strategy`
 */
inherits(Strategy, passport.Strategy);

/**
 * Authenticate request based on the contents of request parameters
 *
 * @param  {Request}    req     The Express Request object that is trying to authenticate
 * @api protected
 */
Strategy.prototype.authenticate = function (request) {
  // Verify and extract the signed body from the request
  AuthenticationSignedUtil.verifySignedAuthenticationBody(
    request.ctx,
    request.body,
    (error, userId, becomeUserId) => {
      if (error) {
        return this.fail(error.msg, error.code);
      }

      // Will hold the user and imposter (if any) for the authentication callback
      let authObject = null;
      let strategyId = null;

      // This is a valid request, get the user and pass it on
      PrincipalsDAO.getPrincipal(userId, (error_, user) => {
        if (error_ && error_.code !== 404) {
          // Ensure there wasn't un unexpected error fetching the user
          return this.error(new Error(error_.msg));
        }

        if (error_ && error_.code === 404) {
          // Ensure the authenticating user exists
          return this.fail(error_.msg, 404);
        }

        if (!becomeUserId) {
          // If the user is not trying to impersonate someone else, we can
          // simply authenticate normally as this user
          strategyId = AuthenticationUtil.getStrategyId(
            user.tenant,
            AuthenticationConstants.providers.SIGNED
          );
          authObject = { user, strategyId };
          AuthenticationUtil.logAuthenticationSuccess(request, authObject, this.name);
          return this.success(authObject);
        }

        // If we get here we are trying to become someone, fetch that person
        // and perform the appropriate permission checks
        PrincipalsDAO.getPrincipal(becomeUserId, (error_, becomeUser) => {
          if (error_ && error_.code !== 404) {
            // Ensure there wasn't un unexpected error fetching the target user
            return this.error(new Error(error_.msg));
          }

          if (error_ && error_.code === 404) {
            // Ensure the impersonated user exists
            return this.fail(error_.msg, 404);
          }

          if (!user.isAdmin(becomeUser.tenant.alias)) {
            // Ensure the authenticated user (impersonator) has the required access to become this user
            return this.fail('You are not authorized to become the target user', 401);
          }

          strategyId = AuthenticationUtil.getStrategyId(
            user.tenant,
            AuthenticationConstants.providers.SIGNED
          );
          authObject = { user: becomeUser, imposter: user, strategyId };
          AuthenticationUtil.logAuthenticationSuccess(request, authObject, this.name);
          return this.success(authObject);
        });
      });
    }
  );
};

/**
 * Expose `Strategy`
 */
export { Strategy as default };