Skip to content

Instantly share code, notes, and snippets.

@edy
Forked from tarlepp/Rights.js
Created August 23, 2014 11:48
Show Gist options
  • Select an option

  • Save edy/761ae36a040b16e6a3d4 to your computer and use it in GitHub Desktop.

Select an option

Save edy/761ae36a040b16e6a3d4 to your computer and use it in GitHub Desktop.

Revisions

  1. @tarlepp tarlepp created this gist Aug 23, 2014.
    129 changes: 129 additions & 0 deletions Rights.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,129 @@
    'use strict';

    var _ = require('lodash');
    var actionUtil = require('sails/lib/hooks/blueprints/actionUtil');

    /**
    * Service function which adds necessary object specified conditions to Project
    * model queries. Workflow with this is following:
    *
    * 1) Fetch project ids where current user is attached to
    * 2) Mutilate current where condition:
    * - if 'id' property exists in query remove not valid projects
    * - if not add 'id' property to query with valid projects
    *
    * @param {Request} request Request object
    * @param {Response} response Response object
    * @param {Function} next Callback function
    */
    exports.makeObjectRightProject = function(request, response, next) {
    sails.log.verbose(' POLICY - ' + __filename);

    // Determine valid project ids for current user
    sails.services['data']
    .getCollectionProperty('projectuser', 'project', {user: request.token}, function(error, validIds) {
    return sails.services['rights']
    .makeObjectCondition(error, validIds, 'id', request, response, next);
    });
    };

    /**
    * Service function which adds necessary object specified conditions to Sprint
    * model queries. Workflow with this is following:
    *
    * 1) Fetch project ids where current user is attached to
    * 2) Mutilate current where condition:
    * - if 'project' property exists in query remove not valid projects
    * - if not add 'project' property to query with valid projects
    *
    * @param {Request} request Request object
    * @param {Response} response Response object
    * @param {Function} next Callback function
    */
    exports.makeObjectRightSprint = function(request, response, next) {
    sails.log.verbose(' POLICY - ' + __filename);

    // Determine valid project ids for current user
    sails.services['data']
    .getCollectionProperty('projectuser', 'project', {user: request.token}, function(error, validIds) {
    return sails.services['rights']
    .makeObjectCondition(error, validIds, 'project', request, response, next);
    });
    };

    /**
    * Generic function which will make necessary object (Project, Sprint, etc.) specified conditions
    * to blueprints query.
    *
    * @param {null|Error} error Possible error object
    * @param {null|[]} validIds Valid id values that are attached to query
    * @param {String} property Model property name where to attach id values
    * @param {Request} request Request object
    * @param {Response} response Response object
    * @param {Function} next Callback function
    *
    * @returns {*}
    */
    exports.makeObjectCondition = function(error, validIds, property, request, response, next) {
    if (error) {
    return response.negotiate(error);
    }

    // Make where condition against specified model property with determined id values
    var where = sails.services['rights']
    .makeConditionValidProperty(validIds, property, request);

    // There is no "valid" ids so we need to send 404 back to client
    if (_.isEmpty(where[property])) {
    error = {
    status: 404
    };

    return response.negotiate(error);
    }

    // Remove existing query
    delete request.query;

    // Set new query to request, that blueprints will use after this
    request.query = {
    where: where
    };

    return next();
    };

    /**
    * Helper function which will mutilate current where condition. Function will add specified
    * property condition with given values to current where object.
    *
    * @param {[]} validIds Valid id values that are attached to current where condition
    * @param {String} property Name of the where condition where to attach id values
    * @param {Request} request Request object
    *
    * @returns {{}} Where object
    */
    exports.makeConditionValidProperty = function(validIds, property, request) {
    // Parse where criteria
    var where = actionUtil.parseCriteria(request);

    // Normalize valid id array
    validIds = _.map(validIds, function(id) {
    return parseInt(id, 10);
    });

    // Specified property is not yet in where query
    if (!where[property]) {
    where[property] = validIds;
    } else { // We have id condition set so we need to check if that / those are allowed
    // Normalize current ids
    var currentIds = _.map((!_.isArray(where[property])) ? [where[property]] : where[property], function (id) {
    return parseInt(id, 10);
    });

    // Remove not valid ids
    where[property] = _.intersection(currentIds, validIds);
    }

    return where;
    };
    59 changes: 59 additions & 0 deletions objectRight.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,59 @@
    'use strict';

    /**
    * Generic object specified (row) policy. Policy is attached to all controllers which are accessible
    * via blueprints API and this policy will call object specified service method to mutate current
    * where object that way that it contains necessary right specified limit conditions.
    *
    * Implementation is pretty straightforward:
    * 1) Create new API
    * 2) Add API specified right checker to 'ObjectRight' service
    * 3) Implement that logic
    * 4) and there is all, happy coding
    *
    * Note that service method structure is always following
    *
    * exports.checkObjectRight[ObjectName] = function(request, response, next) {
    * // Parse where criteria
    * var where = actionUtil.parseCriteria(request);
    *
    * here do your stuff for where object, basically add your business logic
    * checks here... Yes and those can be complicated - I know . good luck :D
    *
    * // Remove existing query
    * delete request.query;
    *
    * // Set new query to request, that blueprints will use after this
    * request.query = {
    * where: where
    * };
    *
    * return next();
    * };
    *
    * @param {Request} request Request object
    * @param {Response} response Response object
    * @param {Function} next Callback function
    *
    * @returns {*}
    */
    module.exports = function(request, response, next) {
    sails.log.verbose(' POLICY - ' + __filename);

    // Determine model and method name
    var model = request.options.model || request.options.controller;
    var method = 'makeObjectRight' + model.charAt(0).toUpperCase() + model.slice(1);

    // Yeah we found actual policy service
    if (typeof sails.services['objectright'][method] === 'function') {
    return sails.services['objectright'][method](request, response, next);
    } else { // Oh noes, is this ok or not?
    var message = 'There is not object specified right handling for \'' + model + '\' model. '
    + 'Please \'' + method + '\' method this to \'ObjectRight\' service.'
    ;

    sails.log.warn(message);

    return next();
    }
    };
    20 changes: 20 additions & 0 deletions policies.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,20 @@
    module.exports.policies = {
    // Default policy for all controllers and actions
    '*': ['passport', 'authenticated'],

    ProjectController: {
    '*': false,
    'find': ['passport', 'authenticated', 'isSocket', 'objectRight'],
    'findOne': ['passport', 'authenticated', 'isSocket', 'objectRight'],
    'create': ['passport', 'authenticated', 'isSocket', 'addDataCreate'],
    'update': ['passport', 'authenticated', 'isSocket', 'addDataUpdate']
    },

    SprintController: {
    '*': false,
    'find': ['passport', 'authenticated', 'isSocket', 'objectRight'],
    'findOne': ['passport', 'authenticated', 'isSocket', 'objectRight'],
    'create': ['passport', 'authenticated', 'isSocket', 'addDataCreate'],
    'update': ['passport', 'authenticated', 'isSocket', 'addDataUpdate']
    }
    };