tao-test/app/tao/views/js/layout/permissions.js

213 lines
7.0 KiB
JavaScript

/**
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; under version 2
* of the License (non-upgradable).
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* Copyright (c) 2018 Open Assessment Technologies SA;
*/
/**
* Manage resources and actions permissions.
* The model is based on actions expecting some permissions (requiredRights) for a given parameter.
* Then each resource has it's own permissions (READ, WRITE, GRANT) that we store.
*
* We can check if a resource has a given permission (can read for example)
* or to validate a context that must contains all required parameters.
*
*
* @author Bertrand Chevrier <bertrand@taotesting.com>
*/
define([
'lodash',
'uri',
], function(_, uriUtil){
'use strict';
/**
* store permissions per resource
* @type {Object}
*/
var permissionStore = {};
/**
* The list of supported rights
* @type {String[]}
*/
var supportedRights = [];
/**
* The permissions manager
* @typedef {Object} permissionsManager
*/
var permissionsManager = {
/**
* set the rights, none by defaults
* @param {String[]} rights
* @returns {permissionsManager} chains
*/
setSupportedRights : function setSupportedRights(rights){
if (_.isArray(rights)) {
supportedRights = _.filter(rights, _.isString);
}
},
/**
* Get the current rights
* @returns {String[]} the rights
*/
getRights : function getRights(){
return supportedRights;
},
/**
* Check if the given right is supported
* @param {String} right - the right to check
* @returns {Boolean}
*/
isSupported : function isSupported(right){
return _.contains(supportedRights, right);
},
/**
* Add permissions to the store.
*
* Polymorphic.
* @example permissionsManager.addPermissions('http://uri.foo/a', ['READ', 'WRITE']);
* @example permissionsManager.addPermissions({
* 'http://uri.foo/a' : ['READ', 'WRITE'],
* 'http://uri.foo/b' : ['READ']
* });
*
*
* @param {String} [uri] - the resource URI
* @param {Array|Object} permissions - either an object where the keys are the URIs or directly the permissions
* @returns {permissionsManager} chains
*/
addPermissions : function addPermissions(uri, permissions){
if(_.isString(uri) && _.isArray(permissions)){
permissionStore[uri] = _.intersection(permissions, _.values(this.getRights()));
}
if(_.isUndefined(permissions) && _.isPlainObject(uri)){
permissions = uri;
_.forEach(permissions, function(value, key){
this.addPermissions(key, value);
}, this);
}
return this;
},
/**
* Retrieve the permissions for the given resource
* @param {String} uri - the resource URI
* @returns {Array} the permissions
*/
getPermissions : function getPermissions(uri){
return permissionStore[uri];
},
/**
* Check if the given resource has the permission
* @param {String} uri - the resource URI
* @param {String} permission - the permission to check
* @returns {Boolean}
*/
hasPermission : function hasPermission(uri, permission){
//if no right is defined, it's open bar
if( supportedRights.length === 0 ) {
return true;
}
if(typeof permissionStore[uri] !== 'undefined'){
return _.contains(permissionStore[uri], permission);
}
return false;
},
/**
* Clear all permissions
* @returns {permissionsManager} chains
*/
clear : function clear(){
permissionStore = {};
return this;
},
/**
* Check if the given context is allowed to execute an action with required rights.
* @param {Object} requiredRights - the action required rights (parameterName : permission)
* @param {Object} resourceContext - the context to verify
* @returns {Boolean}
*/
isContextAllowed : function isContextAllowed(requiredRights, resourceContext){
var self = this;
if(! requiredRights || _.size(requiredRights) === 0 || supportedRights.length === 0){
return true;
}
if(!_.isPlainObject(resourceContext)){
return false;
}
return _.all(requiredRights, function(right, requiredParameter){
var parameterValue;
// translate muti-id into single-id
if (requiredParameter === 'ids') {
requiredParameter = 'id';
}
if(typeof resourceContext[requiredParameter] === 'undefined' || !self.isSupported(right)){
return false;
}
//some values in the context are still URI encoded
parameterValue = uriUtil.decode(resourceContext[requiredParameter]);
return self.hasPermission(parameterValue, right);
});
},
/**
* For a given resource compute the permission mode:
* - allowed : no supported rights required or has the permission on all supported rights
* - partial : has the permission only on some supported rights
* - denied : has the permission on none of the supported rights
* @param {String} uri - the resource URI
* @returns {String} the mode
*/
getResourceAccessMode : function getResourcesAccessMode(uri) {
var self = this;
var mode = 'allowed';
var rights = this.getRights();
var count = _.reduce(rights, function(acc, right){
if(self.hasPermission(uri, right)){
acc++;
}
return acc;
}, 0);
if (rights.length > 0 && count !== rights.length) {
if(count === 0){
mode = 'denied';
} else {
mode = 'partial';
}
}
return mode;
}
};
return permissionsManager;
});