Start to build the ACL models
This commit is contained in:
parent
492aca7724
commit
67b934357b
|
@ -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);
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
|
@ -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
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue