/**
* Plugin for Baucis to configure read/write access on per-attribute level.
*
* @package baucis-access
* @author Pavel Karoukin <pavel@karoukin.us>
*/
var baucis = require('baucis');
var deepExtend = require('deep-extend');
/**
* Plugin for Baucis to configure read/write access on per-attribute level.
*
* @package baucis-access
* @author Pavel Karoukin <pavel@karoukin.us>
*/
var baucis = require('baucis');
var deepExtend = require('deep-extend');
Example of settings up baucis-access plugin:
var controller = baucis.rest(‘model’);
controller.access({ roles: { anonymous: { read: true } } });
Following options are available:
var access = module.exports = function(controller) {
return {
writeException - set to false to NOT throw an exception when client tries to write attributes not allowed for current role and silently ignore these
writeException: true,
getCustomRole - allows you to define custom roles outside of standard ‘anonymous’, ‘authenticated’, ‘owner’. Should return new array of roles.
req - Express’s request object roles - current list of roles
getCustomRole: function(req, roles) {
return roles;
},
getRoles - Allows you to override function used to get current user roles
getRoles: function(req) {
var roles = [];
if (! req.user) {
roles.push('anonymous');
}
else {
roles.push('authenticated');
}
if (access.getCustomRole) {
roles = this.getCustomRole.apply(controller, [req, roles]);
}
return roles;
},
modelUserId - Name of the model’s attribute where user_id is stored. Defaults to user_id.
modelUserId: 'user_id',
requestUserId - Name of attribute inside req.user object with currently logged in user_id. Defauls to _id.
requestUserId: '_id',
roles - Rules based on role and create/write/read operation. Last rule have precedence over rules above. I.e. if user have both ‘authenticated’ and ‘owner’ roles, settings for ‘owner’ role will prevail.
roles: {
defaults: {
create: false,
read : false,
write : false
},
anonymous: {
create: false,
write : false,
read : false
},
authenticated: {
create: true,
read : true,
write : false
},
owner: {
read : true,
write : true
}
},
getRules - returns rules based on roles.
getRules: function(roles) {
var that = this;
var rules = this.roles.defaults;
roles.forEach(function(role) {
rules = deepExtend(rules, that.roles[role]);
});
return rules;
},
getCurrentRules - returns rules for current user.
getCurrentRules: function(req, model) {
var roles = this.getRoles(req);
if (model && roles.indexOf('owner') == -1 && req.user && this.modelUserId && this.requestUserId) {
var model_user_id = model[this.modelUserId] + "";
var req_user_id = req.user[this.requestUserId] + "";
if (model_user_id && req_user_id && model_user_id == req_user_id) {
roles.push('owner');
}
}
return this.getRules(roles);
}
};
};
baucis.Controller.decorators(function (model, protect) {
var controller = this;
protect.property('access', new access(controller), function(options) {
return deepExtend({ enabled: true }, new access(controller), options);
});
Make sure creation of new records allowed for current role.
controller.request('post', function(req, res, next) {
if (! controller.access().enabled) {
return next();
}
var rules = controller.access().getCurrentRules(req);
if (! rules.create) {
return res.status(403).send(baucis.Error.Forbidden('Creating this type of documents is prohibited.'));
}
else {
next();
}
});
controller.request('put post', function(req, res, next) {
if (! controller.access().enabled) {
return next();
}
var error = false;
req.baucis.incoming(function(ctx, cb) {
if (error) {
return;
}
var doc = ctx.doc;
var newDoc = ctx.incoming;
var rules = controller.access().getCurrentRules(req, doc);
if (rules.write === true) {
return cb(null, ctx);
}
if (rules.write === false) {
res.status(403).send(baucis.Error.Forbidden('Writing to this document is prohibited.'));
error = true;
return;
}
var allowedFields = rules.write;
if (! Array.isArray(allowedFields)) {
allowedFields = allowedFields.split(/\s+/);
}
Object.keys(newDoc).forEach(function(attr) {
if (allowedFields.indexOf(attr) === -1) {
if (controller.access().writeException) {
res.status(403).send(baucis.Error.Forbidden('Attempt to update read-only field.'));
error = true;
return;
}
delete newDoc[attr];
}
});
cb(null, ctx);
});
next();
});
});