Start to build the ACL models

This commit is contained in:
Raymond Feng 2013-11-09 22:22:16 -08:00
parent 492aca7724
commit 67b934357b
5 changed files with 210 additions and 63 deletions

View File

@ -90,7 +90,7 @@ loopback.errorHandler.title = 'Loopback';
*/ */
loopback.createDataSource = function (name, options) { loopback.createDataSource = function (name, options) {
var ds = new DataSource(name, options); var ds = new DataSource(name, options, loopback.Model.dataSource);
ds.createModel = function (name, properties, settings) { ds.createModel = function (name, properties, settings) {
var ModelCtor = loopback.createModel(name, properties, settings); var ModelCtor = loopback.createModel(name, properties, settings);
ModelCtor.attachTo(ds); ModelCtor.attachTo(ds);

View File

@ -1,14 +1,14 @@
/** /**
Schema ACL options Schema ACL options
Object level permissions, for example, an album owned by a user Object level permissions, for example, an album owned by a user
Factors to be authorized against: Factors to be authorized against:
* model name: Album * model name: Album
* model instance properties: userId of the album, friends, shared * model instance properties: userId of the album, friends, shared
* methods * methods
* app and/or user ids/roles * app and/or user ids/roles
** loggedIn ** loggedIn
** roles ** roles
** userId ** userId
@ -17,69 +17,127 @@ Factors to be authorized against:
** everyone ** everyone
** relations: owner/friend/granted ** relations: owner/friend/granted
Class level permissions, for example, Album Class level permissions, for example, Album
* model name: Album * model name: Album
* methods * methods
URL/Route level permissions URL/Route level permissions
* url pattern * url pattern
* application id * application id
* ip addresses * ip addresses
* http headers * http headers
Map to oAuth 2.0 scopes Map to oAuth 2.0 scopes
*/ */
var loopback = require('loopback'); var loopback = require('../loopback');
var ACLEntrySchema = { var ScopeSchema = {
/** name: {type: String, required: true},
* Type of the principal - Application/User/Role description: String
*/
principalType: String,
/**
* Id of the principal - such as appId, userId or roleId
*/
principalId: String,
/**
* Name of the access type - READ/WRITE/EXEC
*/
accessType: String,
/**
* ALARM - Generate an alarm, in a system dependent way, the access specified in the permissions component of the ACL entry.
* ALLOW - Explicitly grants access to the resource.
* AUDIT - Log, in a system dependent way, the access specified in the permissions component of the ACL entry.
* DENY - Explicitly denies access to the resource.
*/
permission: String
}; };
var AccessSchema = { var ScopeResourceAccessSchema = {
publicReadAccess: Boolean, model: String, // The name of the model
publicWriteAccess: Boolean, property: String, // The name of the property, method, scope, or relation
publicExecAccess: Boolean,
permissions: [ACLEntrySchema] /**
* Name of the access type - READ/WRITE/EXEC
*/
accessType: String,
/**
* ALARM - Generate an alarm, in a system dependent way, the access specified in the permissions component of the ACL entry.
* ALLOW - Explicitly grants access to the resource.
* AUDIT - Log, in a system dependent way, the access specified in the permissions component of the ACL entry.
* DENY - Explicitly denies access to the resource.
*/
permission: String,
scopeId: Number
}; };
var ScopeResourceAccess = loopback.createModel('ScopeResourceAccess', ScopeResourceAccessSchema, {
relations: {
scope: {
type: 'belongsTo',
model: 'Scope',
foreignKey: 'scopeId'
}
}
});
/**
* Scope has many resource access entries
* @type {createModel|*}
*/
var Scope = loopback.createModel('Scope', ScopeSchema, {
relations: {
resources: {
type: 'hasMany',
model: 'ScopeResourceAccess',
foreignKey: 'scopeId'
}
}
});
var ACLSchema = { var ACLSchema = {
/** model: String, // The name of the model
* Resource property: String, // The name of the property, method, scope, or relation
*/
model: String, // The name of the model
property: String, // The name of the property
method: String, // The name of the method
access: AccessSchema, // The access /**
* Name of the access type - READ/WRITE/EXEC
*/
accessType: String,
status: String, /**
created: Date, * ALARM - Generate an alarm, in a system dependent way, the access specified in the permissions component of the ACL entry.
modified: Date * ALLOW - Explicitly grants access to the resource.
* AUDIT - Log, in a system dependent way, the access specified in the permissions component of the ACL entry.
* DENY - Explicitly denies access to the resource.
*/
permission: String,
/**
* Type of the principal - Application/User/Role
*/
principalType: String,
/**
* Id of the principal - such as appId, userId or roleId
*/
principalId: String
}; };
var ACL = loopback.createModel('ACL', ACLSchema); var ACL = loopback.createModel('ACL', ACLSchema);
module.exports = ACL; module.exports = {
ACL: ACL,
Scope: Scope,
ScopeResourceAccess: ScopeResourceAccess
};
Scope.isAllowed = function (scope, model, property, accessType, callback) {
Scope.findOne({where: {name: scope}}, function (err, scope) {
if (err) {
callback && callback(err);
} else {
scope.resources({where: {model: model, property: {inq: [property, '*']}, accessType: {inq: [accessType, '*']}}}, function (err, resources)
{
if (err) {
callback && callback(err);
} else {
console.log('Resources: ', resources);
for (var r = 0; r < resources.length; r++) {
if (resources[r].permission === 'Allow') {
callback && callback(null, true);
return;
}
}
callback && callback(null, false);
}
}
)
;
}
});
};

View File

@ -1,23 +1,43 @@
var loopback = require('loopback'); var loopback = require('../loopback');
// Role model // Role model
var RoleSchema = { var RoleSchema = {
id: {type: String, id: true}, // Id id: {type: String, id: true}, // Id
name: {type: String, required: true}, // The name of a role name: {type: String, required: true}, // The name of a role
description: String, // Description description: String, // Description
roles: [String], // A role can be an aggregate of other roles // roles: [String], // A role can be an aggregate of other roles
users: [String], // A role contains a list of user ids // users: [String], // A role contains a list of user ids
// Timestamps parent: String,
created: {type: Date, default: Date}, // Timestamps
modified: {type: Date, default: Date} created: {type: Date, default: Date},
modified: {type: Date, default: Date}
}; };
var Role = loopback.createModel('Role', RoleSchema); var Role = loopback.createModel('Role', RoleSchema, {
relations: {
roles: {
type: 'hasMany',
model: 'Role',
foreignKey: 'parent'
},
users: {
type: 'hasAndBelongsToMany',
model: 'user',
foreignKey: 'userId'
},
applications: {
type: 'hasAndBelongsToMany',
model: 'Application',
foreignKey: 'appId'
}
}
});
module.exports = Role; module.exports = Role;
Role.OWNER ='$owner'; // owner of the object // Special roles
Role.OWNER = '$owner'; // owner of the object
Role.RELATED = "$related"; // any User with a relationship to the object Role.RELATED = "$related"; // any User with a relationship to the object
Role.AUTHENTICATED = "$authenticated"; // authenticated user Role.AUTHENTICATED = "$authenticated"; // authenticated user
Role.EVERYONE = "$everyone"; // everyone Role.EVERYONE = "$everyone"; // everyone

28
test/acl.test.js Normal file
View File

@ -0,0 +1,28 @@
var assert = require('assert');
var loopback = require('../index');
var acl = require('../lib/models/acl');
var User = loopback.User;
describe('security scopes', function () {
it("should allow access to models", function () {
var ds = loopback.createDataSource({connector: loopback.Memory});
acl.Scope.attachTo(ds);
acl.ScopeResourceAccess.attachTo(ds);
// console.log(acl.Scope.relations);
acl.Scope.create({name: 'user', description: 'access user information'}, function (err, scope) {
console.log(scope);
scope.resources.create({model: 'user', property: '*', accessType: '*', permission: 'Allow'}, function (err, resource) {
console.log(resource);
acl.Scope.isAllowed('user', 'user', '*', '*', console.log);
});
});
});
});

41
test/role.test.js Normal file
View File

@ -0,0 +1,41 @@
var assert = require('assert');
var loopback = require('../index');
var Role = require('../lib/models/role');
var User = loopback.User;
describe('security models', function () {
describe('roles', function () {
it("Defines role/role relations", function () {
var ds = loopback.createDataSource({connector: loopback.Memory});
Role.attachTo(ds);
Role.create({name: 'user'}, function (err, role) {
role.roles.create({name: 'admin'}, function (err, role2) {
Role.find(console.log);
role.roles(console.log);
});
});
});
it("Defines role/user relations", function () {
var ds = loopback.createDataSource({connector: loopback.Memory});
User.attachTo(ds);
Role.attachTo(ds);
Role.create({name: 'user'}, function (err, role) {
role.users.create({name: 'Raymond'}, function (err, user) {
console.log('User: ', user);
Role.find(console.log);
role.users(console.log);
});
});
});
});
});