SimpliField/angular-reaccess

View on GitHub
src/index.js

Summary

Maintainability
D
1 day
Test Coverage

const moduleName = 'simplifield.reaccess';

export default angular
  .module(moduleName, ['ng'])
  .provider('sfReaccessService', SFReaccessServiceProvider)
  .filter('sfReaccess', ['$log', 'sfReaccessService', sfReaccessFilter])
  .name;

function SFReaccessServiceProvider() {
  var $injector = angular.injector(['ng']);
  var predefinedRights = {};
  var currentRights = [];
  var currentValues = [];
  var globalDebug = false;

  this.setPredefinedRights = function(value) {
    predefinedRights = value;
  };

  this.addPredefinedRights = function(value) {
    predefinedRights = angular.extend({}, predefinedRights, value);
  };

  this.debug = function(value) {
    globalDebug = value;
  };

  this.$get = ['$log', function SFReaccessServiceFactory($log) {
    var sfReaccessService = {
      setRights: function sfReaccessServiceSetRights(rights) {
        currentRights = rights;
      },
      setValues: function sfReaccessServiceSetValues(values) {
        currentValues = values;
      },
      _debugging: globalDebug,
      match: function sfReaccessServiceMatch(predefinedRight, templateValues, debug) {
        var rights;
        var right;
        var path;
        var rightPath;
        try {
          rights = predefinedRights[predefinedRight] instanceof Array ?
            predefinedRights[predefinedRight] :
            [predefinedRights[predefinedRight]];
          return rights.every(function(right) {
            if((!right) || 'undefined' == typeof right.path
              || 'undefined' == typeof right.methods
              || !right.methods.length) {
              if(debug) {
                $log.error('sfReaccess: ' + predefinedRight + ': No or invalid right' +
                  ' object (requiring path, methods properties).');
              }
              return false;
            }
            rightPath = right.path;
            while(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/.test(rightPath)) {
              rightPath = rightPath.replace(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/,
                function($, $1, $2, $3) {
                  var values = getValues(templateValues.concat(currentValues), $2);
                  if(debug) {
                    $log.debug('sfReaccess: ' + predefinedRight + ': Found the' +
                      ' templated value "' + $2 + '" in the predefined right,' +
                      ' resolved it to the following values:', values, templateValues.concat(currentValues));
                  }
                  if(values.length) {
                    return $1 + values[0] + $3;
                  }
                  return '';
                });
            }
            if(currentRights.some(function(currentRight, i) {
              var result = false;
              if(!right.methods.every(function(method) {
                return -1 !== currentRight.methods.indexOf(method);
              })) {
                if(debug) {
                  $log.debug('sfReaccess: ' + predefinedRight + ': ' + i + ': Methods' +
                    ' doesn\'t match.', right.methods, currentRight.methods);
                }
                return false;
              }
              path = currentRight.path;
              if(currentValues) {
                while(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/.test(path)) {
                  path = path.replace(/(.*\/|^):([a-z0-9_\-\.\*\@\#]+)(\/.*|$)/,
                    function($, $1, $2, $3) {
                      var values = getValues(currentValues, $2);
                      if(debug) {
                        $log.debug('sfReaccess: ' + predefinedRight + ': ' + i +
                          ' Found the templated value "' + $2 + '" in the current' +
                          ' tested right, resolved it to the following values:', values, currentValues);
                      }
                      if(values.length) {
                        return $1 + (1 === values.length ?
                          escRegExp(values[0]) :
                          '(' + values.map(escRegExp).join('|') + ')') + $3;
                      }
                      return '';
                    });
                }
              }
              result = path && new RegExp('^'+path+'$').test(rightPath)
              if(debug) {
                $log.debug('sfReaccess: ' + predefinedRight + ': ' + i + ': Testing "' +
                  rightPath + '" against "/^' + path.replace('/', '\\/') + '$/"' +
                  ' (original path "' + currentRight.path + '")' +
                  ' led to ', result ? 'SUCCESS' : 'FAILURE');
              }
              return result;
            })) {
              return true;
            }
            return null;
          }) ? rightPath : null;
        } catch(err) {
          if(debug) {
            $log.debug('sfReaccess: ' + predefinedRight + ': FAILURE due "' +
              'to the following error:', err);
          }
          return null;
        }
      },
      test: function sfReaccessServiceTest() {
        var res = sfReaccessService.match.apply(sfReaccessService, arguments);

        return (res !== null);
      }
    };

    return sfReaccessService;
  }];
}

function sfReaccessFilter($log, sfReaccessService) {
  return function(predefinedRights, templateValues, debug) {
    debug = 'boolean' === typeof debug ? debug : sfReaccessService._debugging;
    predefinedRights = predefinedRights || [];
    templateValues = (templateValues instanceof Array ? templateValues :
      (templateValues ? [templateValues] : []));
    if(!(predefinedRights instanceof Array)) {
      predefinedRights = [predefinedRights];
    }
    if(debug) {
      $log.debug('sfReaccess: New filter execution:', predefinedRights, templateValues);
    }
    return predefinedRights.every(function(predefinedRight) {
      return sfReaccessService.test(predefinedRight, templateValues, debug);
    });
  };
}

// Helpers
function getValues(values, path) {
  var index = path.indexOf('.');
  var part = -1 !== index ? path.substring(0, index) : path;
  path = -1 !== index ? path.substring(index + 1) : '';

  values = values.reduce(function(values, value) {
    if((value instanceof Object) && '*' === part) {
      values = values.concat(Object.keys(value).map(function(key) {
        return value[key];
      }));
    }
    if((value instanceof Object) && '@' === part) {
      values = values.concat(Object.keys(value).filter(function(key) {
        return /^[^0-9]+$/.test(key);
      }).map(function(key) {
        return value[key];
      }));
    }
    if((value instanceof Array) && '#' === part) {
      values = values.concat(value);
    }
    if(-1 === ['@', '#', '*'].indexOf(part) &&
      'undefined' !== typeof value[part]) {
      values.push(value[part]);
    }
    return values;
  }, []).filter(function(value) {
    return 'undefined' !== typeof value;
  });
  return '' === path ? values : getValues(values, path);
}

function escRegExp(str) {
  return String(str).replace(/([.*+?=^!:${}()|[\]\/\\])/g, '\\$1');
}