linagora/openpaas-esn

View on GitHub
backend/webserver/middleware/domain.js

Summary

Maintainability
F
3 days
Test Coverage
'use strict';

const q = require('q');
const emailAddresses = require('email-addresses');
const Domain = require('mongoose').model('Domain');
const dbHelper = require('../../helpers').db;
const userIndex = require('../../core/user/index');
const coreDomain = require('../../core/domain');
const logger = require('../../core/logger');
const authorize = require('../middleware/authorization');
const emailChecker = require('email-addresses');

module.exports = {
  canGetMembers,
  load,
  loadFromDomainIdParameter,
  loadDomainByHostname,
  loadSessionDomain,
  requireAdministrator,
  requireDomainInfo,
  checkUpdateParameters,
  checkMemberAccounts
};

/**
 * Load domain by hostname of request
 * @param  {Request}   req
 * @param  {Response}  res
 * @param  {Function}  next
 */
function loadDomainByHostname(req, res, next) {
  const hostname = req.hostname;

  coreDomain.getByHostname(hostname)
    .then(domain => {
      if (domain) {
        req.domain = domain;
        next();
      } else {
        res.status(404).json({
          error: {
            code: 404,
            message: 'Not Found',
            details: `No domain found for hostname: ${hostname}`
          }
        });
      }
    },
    err => {
      const details = `Error while getting domain by hostname ${hostname}`;

      logger.error(details, err);

      res.status(500).json({
        error: {
          code: 500,
          message: 'Server Error',
          details
        }
      });
    });
}

/**
 * Load middleware. Load a domain from its UUID and push it into the request (req.domain) for later use.
 *
 * @param {Request} req
 * @param {Response} res
 * @param {Function} next
 */
function load(req, res, next) {
  const { uuid } = req.params;

  if (!dbHelper.isValidObjectId(uuid)) {
    return res.status(400).json({ error: { code: 400, message: 'Bad request', details: 'Invalid domain id' }});
  }

  Domain.loadFromID(uuid, function(err, domain) {
    if (err) {
      return next(err);
    }
    if (!domain) {
      return res.status(404).json({
        error: {
          code: 404,
          message: 'Not Found',
          details: `No domain found for id: ${uuid}`
        }
      });
    }
    req.domain = domain;

    return next();
  });
}

function loadFromDomainIdParameter(req, res, next) {
  const id = req.query.domain_id;

  if (!id) {
    return res.status(400).json({ error: { code: 400, message: 'Missing parameter', details: 'The domain_id parameter is mandatory'}});
  }

  if (!dbHelper.isValidObjectId(id)) {
    return res.status(400).json({ error: { code: 400, message: 'Bad Request', details: 'Invalid domain_id parameter' }});
  }

  loadDomain(id)(req, res, next);
}

/**
 * Require an domain information middleware.
 *
 * @param {Request} req
 * @param {Response} res
 * @param {Function} next
 */
function requireDomainInfo(req, res, next) {
  let details;

  if (!req.body.name) {
    details = 'Domain does not have name';
  } else if (!req.body.company_name) {
    details = 'Domain does not have company name';
  }

  if (details) {
    return res.status(400).json({
      error: {
        code: 400,
        message: 'Bad Request',
        details
      }
    });
  }

  if (req.body.hostnames) {
    if (!Array.isArray(req.body.hostnames)) {
      return res.status(400).json({
        error: {
          code: 400,
          message: 'Bad Request',
          details: 'Hostnames must be an array!'
        }
      });
    }

    return ensureNoConflictHostname(req, res, next);
  }

  next();
}

/**
 * Load preferred domain of the authenticated user
 * @param {Request} req
 * @param {Response} res
 * @param {Function} next
 */
function loadSessionDomain(req, res, next) {
  const domainId = req.user.preferredDomainId;

  if (!domainId) {
    return res.status(404).json({
      error: {
        code: 404,
        message: 'Not Found',
        details: 'You do not belong to any domain'
      }
    });
  }

  loadDomain(domainId)(req, res, next);
}

function loadDomain(domainId) {
  return (req, res, next) => {
    Domain.loadFromID(domainId, function(err, domain) {
      if (err) {
        return next(err);
      }

      if (!domain) {
        return res.status(404).json({
          error: {
            code: 404,
            message: 'Not found',
            details: `The domain ${domainId} could not be found`}
          });
      }

      req.domain = domain;

      return next();
    });
  };
}

/**
 * Require an administrator with well-formed middleware.
 *
 * @param {Request} req
 * @param {Response} res
 * @param {Function} next
 */
function requireAdministrator(req, res, next) {
  const administrator = req.body.administrator;
  let error, details;

  if (!administrator) {
    details = 'An administrator is required';
  } else if (!administrator.email) {
    details = 'Administrator does not have any email address';
  } else if (!administrator.password) {
    details = 'Administrator does not have password';
  } else if (!_isValidEmail(administrator.email)) {
    details = 'Administrator email is not valid';
  }

  if (details) {
    error = {
      code: 400,
      message: 'Bad Request',
      details
    };

    return res.status(error.code).json({ error });
  }

  return userIndex.findByEmail(administrator.email, (err, user) => {
    if (err) {
      return next(err);
    }

    if (user) {
      details = 'Administrator email is already used';
      error = {
        code: 409,
        message: 'Conflict',
        details
      };

      return res.status(error.code).json({ error });
    }

    next();
  });
}

/**
 * Middleware checks parameters for update domain API.
 *
 * @param {Request} req
 * @param {Response} res
 * @param {Function} next
 */
function checkUpdateParameters(req, res, next) {
  if (!req.body.company_name && !req.body.hostnames) {
    return res.status(400).json({
      error: {
        code: 400,
        message: 'Bad Request',
        details: 'Company name or hostnames are required'
      }
    });
  }

  if (req.body.hostnames) {
    if (!Array.isArray(req.body.hostnames)) {
      return res.status(400).json({
        error: {
          code: 400,
          message: 'Bad Request',
          details: 'Hostnames must be an array!'
        }
      });
    }

    return ensureNoConflictHostname(req, res, next);
  }

  return next();
}

function checkMemberAccounts(req, res, next) {
  const accountList = req.body.accounts;

  const errorMessage = {
    error: {
      code: 400,
      message: 'Bad Request'
    }
  };

  if (!accountList) {
    errorMessage.error.details = 'Accounts field is required in payload';

    return res.status(400).json(errorMessage);
  }

  if (!Array.isArray(accountList) || accountList.length === 0) {
    errorMessage.error.details = 'Accounts field must be an array with at least 1 element';

    return res.status(400).json(errorMessage);
  }

  const hasEmail = accountList.some(account => account.emails && account.emails.some(email => !!email));

  if (!hasEmail) {
    errorMessage.error.details = 'A member must have at least 1 email';

    return res.status(400).json(errorMessage);
  }

  const isValidEmail = accountList.every(account => {
    account.emails = Array.isArray(account.emails) ? account.emails : [account.emails];

    return account.emails.every(email => emailChecker(email));
    });

  if (!isValidEmail) {
    errorMessage.error.details = 'Emails must be in correct format';

    return res.status(400).json(errorMessage);
  }

  return next();
}

function ensureNoConflictHostname(req, res, next) {
  const hostnames = req.body.hostnames;

  q.all(hostnames.map(hostname => coreDomain.getByHostname(hostname)))
    .then(domains => {
      const isUsedByOtherDomain = domains.findIndex(domain => domain && String(domain._id) !== req.params.uuid);

      if (isUsedByOtherDomain !== -1) {
        return res.status(409).json({
          error: {
            code: 409,
            message: 'Conflict',
            details: `Hostname ${hostnames[isUsedByOtherDomain]} is already in use`
          }
        });
      }

      return next();
    })
    .catch(err => {
      logger.error('Unable to verify hostnames', err);

      return res.status(500).json({
        error: {
          code: 500,
          message: 'Server Error',
          details: 'Unable to verify hostnames'
        }
      });
    });
}

function canGetMembers(req, res, next) {
  if (req.query.includesDisabledSearchable === 'true' || req.query.ignoreMembersCanBeSearchedConfiguration === 'true') {
    return authorize.requiresDomainManager(req, res, next);
  }

  return authorize.requiresDomainMember(req, res, next);
}

function _isValidEmail(email) {
  return emailAddresses.parseOneAddress(email) !== null;
}