text: // Copyright 2016, EMC, Inc.
  
  'use strict';
  
  var di = require('di'),
      nodeAcl = require( 'acl' );
  
  module.exports = accountApiServiceFactory;
  di.annotate(accountApiServiceFactory, new di.Provide('Http.Services.Api.Account'));
  di.annotate(accountApiServiceFactory,
      new di.Inject(
          'Services.Waterline',
          'Errors',
          'Logger',
          '_',
          'Promise',
          'Constants',
          'Services.Configuration',
          'Assert'
      )
  );
  function accountApiServiceFactory(
      waterline,
      Errors,
      Logger,
      _,
      Promise,
      Constants,
      configuration,
      assert
  ) {
      var logger = Logger.initialize(accountApiServiceFactory);
  
      function AccountApiService() {
          this.acl = new nodeAcl(new nodeAcl.memoryBackend());  // jshint ignore:line
      }
  
      AccountApiService.prototype.start = function() {
          var acl = this.acl;
          var self = this;
          var endpoints = configuration.get('httpEndpoints', []);
          var authEnabled = _(endpoints).filter(function(endpoint) {
              return endpoint.authEnabled === true;
          }).compact().value();
  
          // Only initialize the service if an endpoint has enabled authn
          if(_.isEmpty(authEnabled)) {
              return;
          }
  
          return Promise.join( self.listUsers(), waterline.roles.find({}), function(users, roles) {
              var promises = _.map(users, function(user) {
                      return acl.addUserRoles(user.username, user.role);
                  });
              var rolePromises = _.map(roles, function(roles) {
                      return acl.addRoleParents(roles.role, roles.privileges);
                  });
              Array.prototype.push.apply(promises, rolePromises);
              return Promise.all(promises);
          }).then(function() {
              // Enforce a subset of hardcoded privileges:
              var operator = {
                  role: 'Operator',
                  privileges: ['Login', 'ConfigureComponents', 'ConfigureSelf']
              };
              var admin = {
                  role: 'Administrator',
                  privileges: ['Read', 'Write', 'Login', 'ConfigureManager',
                               'ConfigureUsers', 'ConfigureComponents',
                               'ConfigureSelf']
              };
              var readOnly = {
                  role: 'ReadOnly',
                  privileges: ['Read', 'Login', 'ConfigureSelf']
              };
              return Promise.all([
                  self.createRole(readOnly),
                  self.createRole(operator),
                  self.createRole(admin)
              ]).catch(function () {
                  //mask error here
              });
          });
      };
  
      AccountApiService.prototype.aclMethod = function(name) {
          var args = Array.prototype.slice.call(arguments, 1);
          return this.acl[name].apply(this.acl, args);
      };
  
      AccountApiService.prototype.listUsers = function() {
          var self = this;
          return Promise.map(waterline.localusers.find({}), function(user) {
              return Promise.props({
                  id: user.id,
                  username: user.username,
                  role: user.role,
                  privileges: self.aclMethod('_rolesParents', [user.role])
              });
          });
      };
  
      AccountApiService.prototype.getUserByName = function(username) {
          var self = this;
          return Promise.try(function() {
              assert.string(username);
              return waterline.localusers.findOne({username: username});
          })
          .then(function(user) {
              if(!user) {
                  throw new Errors.NotFoundError();
              }
              return Promise.props({
                  id: user.id,
                  username: user.username,
                  role: user.role,
                  privileges: self.aclMethod('_rolesParents', [user.role])
              });
          });
      };
  
      AccountApiService.prototype.createUser = function(user) {
          var self = this;
          return Promise.try(function() {
              assert.string(user.username);
              return waterline.localusers.findOne({username: user.username});
          })
          .then(function(entry) {
              if(entry) {
                  // TODO: Replace with HttpError after errors are refactored
                  var err = new Errors.BaseError('the user already exists');
                  err.status = 409;
                  throw err;
              }
              return waterline.localusers.create({
                  username: user.username,
                  password: user.password,
                  role: user.role
              });
          })
          .then(function(entry) {
              return self.aclMethod('addUserRoles', entry.username, entry.role);
          })
          .then(function() {
              return self.getUserByName(user.username);
          });
      };
  
      AccountApiService.prototype.modifyUserByName = function(name, info) {
          var self = this;
          return Promise.try(function() {
              assert.string(name);
              return waterline.localusers.findOne({username: name});
          })
          .then(function(entry) {
              if(!entry) {
                  throw new Errors.NotFoundError();
              }
              if(info.role && info.role !== entry.role) {
                  return [entry, self.aclMethod('removeUserRoles', entry.username, entry.role)];
              }
              return [ entry ];
          })
          .spread(function(entry) {
              return [entry, waterline.localusers.update({username: name}, info)];
          })
          .spread(function(entry) {
              if(info.role && info.role !== entry.role) {
                  return [entry, self.aclMethod('addUserRoles', entry.username, info.role)];
              }
              return [entry];
          })
          .then(function() {
              return self.getUserByName(name);
          });
      };
  
      AccountApiService.prototype.removeUserByName = function(name) {
          var self = this;
          return Promise.try(function() {
              assert.string(name);
          })
          .then(function() {
              return waterline.localusers.findOne({username: name});
          })
          .then(function(entry) {
              if(!entry) {
                  throw new Errors.NotFoundError();
              }
              return Promise.all([
                  self.aclMethod('removeUserRoles', entry.username, entry.role),
                  waterline.localusers.destroy({username: name})
              ]);
          })
          .then(function() {
              return {
                  username: name
              };
          });
      };
  
      AccountApiService.prototype.createRole = function (newrole) {
          var self = this;
          var acl = this.acl;
          return Promise.try(function () {
              assert.string(newrole.role);
              return waterline.roles.create({
                  role: newrole.role,
                  privileges: newrole.privileges
              });
          })
          .then(function (entry) {
              return acl.addRoleParents(entry.role, entry.privileges);
          })
          .then(function () {
              return self.getRoleByName(newrole.role);
          })
          .catch(function(err) {
              if (err.message.match(/A record with that `role` already exists/)) {
                  err = new Errors.BaseError('role already exists');
                  err.status = 409;
              }
              throw err;
          });
      };
  
      AccountApiService.prototype.getRoleByName = function (name) {
          var self = this;
          return Promise.try(function () {
              assert.string(name);
              return waterline.roles.findOne({ role: name });
          })
          .then(function (roleFound) {
              if (!roleFound) {
                  throw new Errors.NotFoundError();
              }
              return Promise.props({
                  role: roleFound.role,
                  privileges: self.aclMethod('_rolesParents', [roleFound.role])
              });
          });
      };
  
      AccountApiService.prototype.listRoles = function () {
          var self = this;
          return Promise.map(waterline.roles.find({}), function (role) {
              return Promise.props({
                  role: role.role,
                  privileges: self.aclMethod('_rolesParents', [role.role])
              });
          });
      };
  
      AccountApiService.prototype.removeRoleByName = function (name) {
          var acl = this.acl;
          return Promise.try(function () {
              assert.string(name);
          })
          .then(function () {
              return waterline.roles.findOne({ role: name });
          })
          .then(function (entry) {
              if (!entry) {
                  throw new Errors.NotFoundError();
              }
              return Promise.all([
                  acl.removeRoleParents(entry.role),
                  waterline.roles.destroy({ role: name })
              ]);
          })
          .then(function () {
              return {
                  role: name
              };
          });
      };
  
      AccountApiService.prototype.modifyRoleByRoleName = function (name, info) {
          var self = this;
          var acl = this.acl;
          return Promise.try(function () {
              assert.string(name);
              return waterline.roles.findOne({ role: name });
          })
          .then(function (entry) {
              if (!entry) {
                  throw new Errors.NotFoundError();
              }
              return acl.removeRoleParents(entry.role);
          })
          .then(function () {
              return waterline.roles.update({ role: name }, info.privileges);
          })
          .then(function () {
              return acl.addRoleParents(name, info.privileges);
          })
          .then(function () {
              return self.getRoleByName(name);
          });
      };
  
      return new AccountApiService();
  }
  