Merge pull request #99 from strongloop/refactor/acl

Improve debug statements for access control
This commit is contained in:
Ritchie Martori 2013-12-16 20:10:41 -08:00
commit 09fbb8a850
2 changed files with 60 additions and 13 deletions

View File

@ -1,5 +1,6 @@
var loopback = require('../loopback'); var loopback = require('../loopback');
var AccessToken = require('./access-token'); var AccessToken = require('./access-token');
var debug = require('debug')('loopback:security:access-context');
/** /**
* Access context represents the context for a request to access protected * Access context represents the context for a request to access protected
@ -95,6 +96,8 @@ AccessContext.prototype.addPrincipal = function (principalType, principalId, pri
} }
} }
this.principals.push(principal); this.principals.push(principal);
debug('adding principal %j', principal);
return true; return true;
}; };
@ -135,6 +138,36 @@ AccessContext.prototype.isAuthenticated = function() {
return !!(this.getUserId() || this.getAppId()); return !!(this.getUserId() || this.getAppId());
}; };
/**
* Print debug info for access context.
*/
AccessContext.prototype.debug = function() {
if(debug.enabled) {
debug('---AccessContext---');
if(this.principals && this.principals.length) {
debug('principals:')
this.principals.forEach(function(principal) {
debug('principal: %j', principal)
});
} else {
debug('principals: %j', this.principals);
}
debug('modelName %s', this.modelName);
debug('modelId %s', this.modelId);
debug('property %s', this.property);
debug('method %s', this.method);
debug('accessType %s', this.accessType);
if(this.accessToken) {
debug('accessToken:')
debug(' id %j', this.accessToken.id);
debug(' ttl %j', this.accessToken.ttl);
}
debug('getUserId() %s', this.getUserId());
debug('isAuthenticated() %s', this.isAuthenticated());
}
}
/** /**
* This class represents the abstract notion of a principal, which can be used * This class represents the abstract notion of a principal, which can be used
* to represent any entity, such as an individual, a corporation, and a login id * to represent any entity, such as an individual, a corporation, and a login id
@ -188,6 +221,15 @@ function AccessRequest(model, property, accessType, permission) {
this.property = property || AccessContext.ALL; this.property = property || AccessContext.ALL;
this.accessType = accessType || AccessContext.ALL; this.accessType = accessType || AccessContext.ALL;
this.permission = permission || AccessContext.DEFAULT; this.permission = permission || AccessContext.DEFAULT;
if(debug.enabled) {
debug('---AccessRequest---');
debug(' model %s', this.model);
debug(' property %s', this.property);
debug(' accessType %s', this.accessType);
debug(' permission %s', this.permission);
debug(' isWildcard() %s', this.isWildcard());
}
} }
/** /**

View File

@ -163,7 +163,6 @@ ACL.getMatchingScore = function getMatchingScore(rule, req) {
* @returns {Object} The effective ACL * @returns {Object} The effective ACL
*/ */
ACL.resolvePermission = function resolvePermission(acls, req) { ACL.resolvePermission = function resolvePermission(acls, req) {
debug('resolvePermission(): %j %j', acls, req);
// Sort by the matching score in descending order // Sort by the matching score in descending order
acls = acls.sort(function (rule1, rule2) { acls = acls.sort(function (rule1, rule2) {
return ACL.getMatchingScore(rule2, req) - ACL.getMatchingScore(rule1, req); return ACL.getMatchingScore(rule2, req) - ACL.getMatchingScore(rule1, req);
@ -198,7 +197,6 @@ ACL.resolvePermission = function resolvePermission(acls, req) {
var res = new AccessRequest(req.model, req.property, req.accessType, var res = new AccessRequest(req.model, req.property, req.accessType,
permission || ACL.DEFAULT); permission || ACL.DEFAULT);
debug('resolvePermission() returns: %j', res);
return res; return res;
}; };
@ -210,7 +208,6 @@ ACL.resolvePermission = function resolvePermission(acls, req) {
* @return {Object[]} An array of ACLs * @return {Object[]} An array of ACLs
*/ */
ACL.getStaticACLs = function getStaticACLs(model, property) { ACL.getStaticACLs = function getStaticACLs(model, property) {
debug('getStaticACLs(): %s %s', model, property);
var modelClass = loopback.getModel(model); var modelClass = loopback.getModel(model);
var staticACLs = []; var staticACLs = [];
if (modelClass && modelClass.settings.acls) { if (modelClass && modelClass.settings.acls) {
@ -223,6 +220,8 @@ ACL.getStaticACLs = function getStaticACLs(model, property) {
accessType: acl.accessType, accessType: acl.accessType,
permission: acl.permission permission: acl.permission
})); }));
staticACLs[staticACLs.length - 1].debug('Adding ACL');
}); });
} }
var prop = modelClass && var prop = modelClass &&
@ -242,7 +241,6 @@ ACL.getStaticACLs = function getStaticACLs(model, property) {
})); }));
}); });
} }
debug('getStaticACLs() returns: %j', staticACLs);
return staticACLs; return staticACLs;
}; };
@ -262,8 +260,6 @@ ACL.getStaticACLs = function getStaticACLs(model, property) {
ACL.checkPermission = function checkPermission(principalType, principalId, ACL.checkPermission = function checkPermission(principalType, principalId,
model, property, accessType, model, property, accessType,
callback) { callback) {
debug('checkPermission(): %s %s %s %s %s', principalType, principalId, model,
property, accessType);
property = property || ACL.ALL; property = property || ACL.ALL;
var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]}; var propertyQuery = (property === ACL.ALL) ? undefined : {inq: [property, ACL.ALL]};
accessType = accessType || ACL.ALL; accessType = accessType || ACL.ALL;
@ -276,8 +272,8 @@ ACL.checkPermission = function checkPermission(principalType, principalId,
var resolved = ACL.resolvePermission(acls, req); var resolved = ACL.resolvePermission(acls, req);
if(resolved && resolved.permission === ACL.DENY) { if(resolved && resolved.permission === ACL.DENY) {
// Fail fast debug('Permission denied by statically resolved permission');
debug('checkPermission(): %j', resolved); debug(' Resolved Permission: %j', resolved);
process.nextTick(function() { process.nextTick(function() {
callback && callback(null, resolved); callback && callback(null, resolved);
}); });
@ -297,11 +293,22 @@ ACL.checkPermission = function checkPermission(principalType, principalId,
var modelClass = loopback.getModel(model); var modelClass = loopback.getModel(model);
resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW; resolved.permission = (modelClass && modelClass.settings.defaultPermission) || ACL.ALLOW;
} }
debug('checkPermission(): %j', resolved);
callback && callback(null, resolved); callback && callback(null, resolved);
}); });
}; };
ACL.prototype.debug = function() {
if(debug.enabled) {
debug('---ACL---')
debug('model %s', this.model);
debug('property %s', this.property);
debug('principalType %s', this.principalType);
debug('principalId %s', this.principalId);
debug('accessType %s', this.accessType);
debug('permission %s', this.permission);
}
}
/** /**
* Check if the given scope is allowed to access the model/property * Check if the given scope is allowed to access the model/property
* @param {String} scope The scope name * @param {String} scope The scope name
@ -335,8 +342,6 @@ Scope.checkPermission = function (scope, model, property, accessType, callback)
* @param {Function} callback * @param {Function} callback
*/ */
ACL.checkAccess = function (context, callback) { ACL.checkAccess = function (context, callback) {
debug('checkAccess(): %j', context);
if(!(context instanceof AccessContext)) { if(!(context instanceof AccessContext)) {
context = new AccessContext(context); context = new AccessContext(context);
} }
@ -414,7 +419,6 @@ ACL.checkAccess = function (context, callback) {
* @param {Boolean} allowed is the request allowed * @param {Boolean} allowed is the request allowed
*/ */
ACL.checkAccessForToken = function (token, model, modelId, method, callback) { ACL.checkAccessForToken = function (token, model, modelId, method, callback) {
debug('checkAccessForToken(): %j %s %s %s', token, model, modelId, method);
assert(token, 'Access token is required'); assert(token, 'Access token is required');
var context = new AccessContext({ var context = new AccessContext({
@ -427,12 +431,13 @@ ACL.checkAccessForToken = function (token, model, modelId, method, callback) {
context.accessType = context.model._getAccessTypeForMethod(method); context.accessType = context.model._getAccessTypeForMethod(method);
context.debug();
ACL.checkAccess(context, function (err, access) { ACL.checkAccess(context, function (err, access) {
if (err) { if (err) {
callback && callback(err); callback && callback(err);
return; return;
} }
debug('checkAccessForToken(): %j', access);
callback && callback(null, access.permission !== ACL.DENY); callback && callback(null, access.permission !== ACL.DENY);
}); });
}; };