mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-01-27 22:49:56 -05:00
Permissions / ACL
- Created Role model - Created Permission model - Linked Users->Roles with a belongsToMany relationship - Linked Permissions to Users and Roles with a belongsToMany relationship - Created permissions helper with functions for initializing and checking permissions (canThis) - Unit tests for lots of things
This commit is contained in:
parent
d047692c73
commit
e6f7c706cb
14 changed files with 793 additions and 7 deletions
|
@ -63,5 +63,71 @@ module.exports = {
|
||||||
"created_by": 1,
|
"created_by": 1,
|
||||||
"updated_by": 1
|
"updated_by": 1
|
||||||
}
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
roles: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Administrator",
|
||||||
|
"description": "Administrators"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Editor",
|
||||||
|
"description": "Editors"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Author",
|
||||||
|
"description": "Authors"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
roles_users: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"role_id": 1,
|
||||||
|
"user_id": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
permissions: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"name": "Edit posts",
|
||||||
|
"action_type": "edit",
|
||||||
|
"object_type": "post"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"name": "Remove posts",
|
||||||
|
"action_type": "remove",
|
||||||
|
"object_type": "post"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"name": "Create posts",
|
||||||
|
"action_type": "create",
|
||||||
|
"object_type": "post"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
|
||||||
|
permissions_roles: [
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"permission_id": 1,
|
||||||
|
"role_id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"permission_id": 2,
|
||||||
|
"role_id": 1
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"permission_id": 3,
|
||||||
|
"role_id": 1
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -47,6 +47,38 @@
|
||||||
t.integer('updated_by');
|
t.integer('updated_by');
|
||||||
}),
|
}),
|
||||||
|
|
||||||
|
knex.Schema.createTable('roles', function (t) {
|
||||||
|
t.increments().primary();
|
||||||
|
t.string('name');
|
||||||
|
t.string('description');
|
||||||
|
}),
|
||||||
|
|
||||||
|
knex.Schema.createTable('roles_users', function (t) {
|
||||||
|
t.increments().primary();
|
||||||
|
t.integer('role_id');
|
||||||
|
t.integer('user_id');
|
||||||
|
}),
|
||||||
|
|
||||||
|
knex.Schema.createTable('permissions', function (t) {
|
||||||
|
t.increments().primary();
|
||||||
|
t.string('name');
|
||||||
|
t.string('object_type');
|
||||||
|
t.string('action_type');
|
||||||
|
t.integer('object_id');
|
||||||
|
}),
|
||||||
|
|
||||||
|
knex.Schema.createTable('permissions_users', function (t) {
|
||||||
|
t.increments().primary();
|
||||||
|
t.integer('user_id');
|
||||||
|
t.integer('permission_id');
|
||||||
|
}),
|
||||||
|
|
||||||
|
knex.Schema.createTable('permissions_roles', function(t) {
|
||||||
|
t.increments().primary();
|
||||||
|
t.integer('role_id');
|
||||||
|
t.integer('permission_id');
|
||||||
|
}),
|
||||||
|
|
||||||
knex.Schema.createTable('settings', function (t) {
|
knex.Schema.createTable('settings', function (t) {
|
||||||
t.increments().primary();
|
t.increments().primary();
|
||||||
t.string('key');
|
t.string('key');
|
||||||
|
@ -63,6 +95,10 @@
|
||||||
return when.all([
|
return when.all([
|
||||||
knex('posts').insert(fixtures.posts),
|
knex('posts').insert(fixtures.posts),
|
||||||
knex('users').insert(fixtures.users),
|
knex('users').insert(fixtures.users),
|
||||||
|
knex('roles').insert(fixtures.roles),
|
||||||
|
knex('roles_users').insert(fixtures.roles_users),
|
||||||
|
knex('permissions').insert(fixtures.permissions),
|
||||||
|
knex('permissions_roles').insert(fixtures.permissions_roles),
|
||||||
knex('settings').insert(fixtures.settings)
|
knex('settings').insert(fixtures.settings)
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
@ -74,8 +110,17 @@
|
||||||
return when.all([
|
return when.all([
|
||||||
knex.Schema.dropTableIfExists("posts"),
|
knex.Schema.dropTableIfExists("posts"),
|
||||||
knex.Schema.dropTableIfExists("users"),
|
knex.Schema.dropTableIfExists("users"),
|
||||||
knex.Schema.dropTableIfExists("settings")
|
knex.Schema.dropTableIfExists("roles"),
|
||||||
]);
|
knex.Schema.dropTableIfExists("settings"),
|
||||||
|
knex.Schema.dropTableIfExists("permissions")
|
||||||
|
]).then(function() {
|
||||||
|
// Drop the relation tables after the model tables?
|
||||||
|
return when.all([
|
||||||
|
knex.Schema.dropTableIfExists("roles_users"),
|
||||||
|
knex.Schema.dropTableIfExists("permissions_users"),
|
||||||
|
knex.Schema.dropTableIfExists("permissions_roles")
|
||||||
|
]);
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
exports.up = up;
|
exports.up = up;
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
},
|
},
|
||||||
|
|
||||||
logError: function (err) {
|
logError: function (err) {
|
||||||
|
err = err || "Unknown";
|
||||||
// TODO: Logging framework hookup
|
// TODO: Logging framework hookup
|
||||||
console.log("Error occurred: ", err.message || err);
|
console.log("Error occurred: ", err.message || err);
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,6 +9,8 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Post: require('./post').Post,
|
Post: require('./post').Post,
|
||||||
User: require('./user').User,
|
User: require('./user').User,
|
||||||
|
Role: require('./role').Role,
|
||||||
|
Permission: require('./permission').Permission,
|
||||||
Setting: require('./setting').Setting,
|
Setting: require('./setting').Setting,
|
||||||
init: function () {
|
init: function () {
|
||||||
return knex.Schema.hasTable('posts').then(null, function () {
|
return knex.Schema.hasTable('posts').then(null, function () {
|
||||||
|
|
31
core/shared/models/permission.js
Normal file
31
core/shared/models/permission.js
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var GhostBookshelf = require('./base'),
|
||||||
|
User = require('./user').User,
|
||||||
|
Role = require('./role').Role,
|
||||||
|
Permission,
|
||||||
|
Permissions;
|
||||||
|
|
||||||
|
Permission = GhostBookshelf.Model.extend({
|
||||||
|
tableName: 'permissions',
|
||||||
|
|
||||||
|
roles: function () {
|
||||||
|
return this.belongsToMany(Role);
|
||||||
|
},
|
||||||
|
|
||||||
|
users: function () {
|
||||||
|
return this.belongsToMany(User);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Permissions = GhostBookshelf.Collection.extend({
|
||||||
|
model: Permission
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Permission: Permission,
|
||||||
|
Permissions: Permissions
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
32
core/shared/models/role.js
Normal file
32
core/shared/models/role.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
(function () {
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var User = require('./user').User,
|
||||||
|
Permission = require('./permission').Permission,
|
||||||
|
GhostBookshelf = require('./base'),
|
||||||
|
Role,
|
||||||
|
Roles;
|
||||||
|
|
||||||
|
Role = GhostBookshelf.Model.extend({
|
||||||
|
tableName: 'roles',
|
||||||
|
|
||||||
|
users: function () {
|
||||||
|
return this.belongsToMany(User);
|
||||||
|
},
|
||||||
|
|
||||||
|
permissions: function () {
|
||||||
|
return this.belongsToMany(Permission);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Roles = GhostBookshelf.Collection.extend({
|
||||||
|
model: Role
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
Role: Role,
|
||||||
|
Roles: Roles
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
|
@ -8,7 +8,9 @@
|
||||||
nodefn = require('when/node/function'),
|
nodefn = require('when/node/function'),
|
||||||
bcrypt = require('bcrypt-nodejs'),
|
bcrypt = require('bcrypt-nodejs'),
|
||||||
Posts = require('./post').Posts,
|
Posts = require('./post').Posts,
|
||||||
GhostBookshelf = require('./base');
|
GhostBookshelf = require('./base'),
|
||||||
|
Role = require('./role').Role,
|
||||||
|
Permission = require('./permission').Permission;
|
||||||
|
|
||||||
User = GhostBookshelf.Model.extend({
|
User = GhostBookshelf.Model.extend({
|
||||||
|
|
||||||
|
@ -18,6 +20,14 @@
|
||||||
|
|
||||||
posts: function () {
|
posts: function () {
|
||||||
return this.hasMany(Posts, 'created_by');
|
return this.hasMany(Posts, 'created_by');
|
||||||
|
},
|
||||||
|
|
||||||
|
roles: function () {
|
||||||
|
return this.belongsToMany(Role);
|
||||||
|
},
|
||||||
|
|
||||||
|
permissions: function () {
|
||||||
|
return this.belongsToMany(Permission);
|
||||||
}
|
}
|
||||||
|
|
||||||
}, {
|
}, {
|
||||||
|
@ -62,6 +72,35 @@
|
||||||
return user;
|
return user;
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
effectivePermissions: function (id) {
|
||||||
|
return this.read({id: id}, { withRelated: ['permissions', 'roles.permissions'] })
|
||||||
|
.then(function (foundUser) {
|
||||||
|
var seenPerms = {},
|
||||||
|
rolePerms = _.map(foundUser.related('roles').models, function (role) {
|
||||||
|
return role.related('permissions').models;
|
||||||
|
}),
|
||||||
|
allPerms = [];
|
||||||
|
|
||||||
|
rolePerms.push(foundUser.related('permissions').models);
|
||||||
|
|
||||||
|
_.each(rolePerms, function (rolePermGroup) {
|
||||||
|
_.each(rolePermGroup, function (perm) {
|
||||||
|
var key = perm.get('action_type') + '-' + perm.get('object_type') + '-' + perm.get('object_id');
|
||||||
|
|
||||||
|
// Only add perms once
|
||||||
|
if (seenPerms[key]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
allPerms.push(perm);
|
||||||
|
seenPerms[key] = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return when.resolve(allPerms);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
160
core/shared/permissions/index.js
Normal file
160
core/shared/permissions/index.js
Normal file
|
@ -0,0 +1,160 @@
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// canThis(someUser).edit.posts([id]|[[ids]])
|
||||||
|
// canThis(someUser).edit.post(somePost|somePostId)
|
||||||
|
|
||||||
|
var _ = require('underscore'),
|
||||||
|
when = require('when'),
|
||||||
|
Models = require('../models'),
|
||||||
|
UserProvider = Models.User,
|
||||||
|
PermissionsProvider = Models.Permission,
|
||||||
|
init,
|
||||||
|
refresh,
|
||||||
|
canThis,
|
||||||
|
CanThisResult,
|
||||||
|
exported;
|
||||||
|
|
||||||
|
// Base class for canThis call results
|
||||||
|
CanThisResult = function () {
|
||||||
|
this.userPermissionLoad = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
CanThisResult.prototype.buildObjectTypeHandlers = function (obj_types, act_type) {
|
||||||
|
var self = this,
|
||||||
|
obj_type_handlers = {};
|
||||||
|
|
||||||
|
// Iterate through the object types, i.e. ['post', 'tag', 'user']
|
||||||
|
_.each(obj_types, function (obj_type) {
|
||||||
|
|
||||||
|
// Create the 'handler' for the object type;
|
||||||
|
// the '.post()' in canThis(user).edit.post()
|
||||||
|
obj_type_handlers[obj_type] = function (modelOrId) {
|
||||||
|
var modelId;
|
||||||
|
|
||||||
|
if (_.isNumber(modelOrId) || _.isString(modelOrId)) {
|
||||||
|
// It's an id already, do nothing
|
||||||
|
modelId = modelOrId;
|
||||||
|
} else if (modelOrId) {
|
||||||
|
// It's a model, get the id
|
||||||
|
modelId = modelOrId.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait for the user loading to finish
|
||||||
|
return self.userPermissionLoad.then(function (userPermissions) {
|
||||||
|
|
||||||
|
// Iterate through the user permissions looking for an affirmation
|
||||||
|
var hasPermission = _.any(userPermissions, function (userPermission) {
|
||||||
|
var permObjId;
|
||||||
|
|
||||||
|
// Look for a matching action type and object type first
|
||||||
|
if (userPermission.get('action_type') !== act_type || userPermission.get('object_type') !== obj_type) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Grab the object id (if specified, could be null)
|
||||||
|
permObjId = userPermission.get('object_id');
|
||||||
|
|
||||||
|
// If we didn't specify a model (any thing)
|
||||||
|
// or the permission didn't have an id scope set
|
||||||
|
// then the user has permission
|
||||||
|
if (!modelId || !permObjId) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, check if the id's match
|
||||||
|
// TODO: String vs Int comparison possibility here?
|
||||||
|
return modelId === permObjId;
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasPermission) {
|
||||||
|
return when.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
return when.reject();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
return obj_type_handlers;
|
||||||
|
};
|
||||||
|
|
||||||
|
CanThisResult.prototype.beginCheck = function (user) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// TODO: Switch logic based on object type; user, role, post.
|
||||||
|
|
||||||
|
// Kick off the fetching of the user data
|
||||||
|
this.userPermissionLoad = UserProvider.effectivePermissions(user.id || user);
|
||||||
|
|
||||||
|
// Iterate through the actions and their related object types
|
||||||
|
// We should have loaded these through a permissions.init() call previously
|
||||||
|
// TODO: Throw error if not init() yet?
|
||||||
|
_.each(exported.actionsMap, function (obj_types, act_type) {
|
||||||
|
// Build up the object type handlers;
|
||||||
|
// the '.post()' parts in canThis(user).edit.post()
|
||||||
|
var obj_type_handlers = self.buildObjectTypeHandlers(obj_types, act_type);
|
||||||
|
|
||||||
|
// Define a property for the action on the result;
|
||||||
|
// the '.edit' in canThis(user).edit.post()
|
||||||
|
Object.defineProperty(self, act_type, {
|
||||||
|
writable: false,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
value: obj_type_handlers
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Return this for chaining
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
|
||||||
|
canThis = function (user) {
|
||||||
|
var result = new CanThisResult();
|
||||||
|
|
||||||
|
return result.beginCheck(user);
|
||||||
|
};
|
||||||
|
|
||||||
|
init = refresh = function () {
|
||||||
|
// Load all the permissions
|
||||||
|
return PermissionsProvider.browse().then(function (perms) {
|
||||||
|
var seenActions = {};
|
||||||
|
|
||||||
|
exported.actionsMap = {};
|
||||||
|
|
||||||
|
// Build a hash map of the actions on objects, i.e
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
'edit': ['post', 'tag', 'user', 'page'],
|
||||||
|
'delete': ['post', 'user'],
|
||||||
|
'create': ['post', 'user', 'page']
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
_.each(perms.models, function (perm) {
|
||||||
|
var action_type = perm.get('action_type'),
|
||||||
|
object_type = perm.get('object_type');
|
||||||
|
|
||||||
|
exported.actionsMap[action_type] = exported.actionsMap[action_type] || [];
|
||||||
|
seenActions[action_type] = seenActions[action_type] || {};
|
||||||
|
|
||||||
|
// Check if we've already seen this action -> object combo
|
||||||
|
if (seenActions[action_type][object_type]) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
exported.actionsMap[action_type].push(object_type);
|
||||||
|
seenActions[action_type][object_type] = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
return when(exported.actionsMap);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = exported = {
|
||||||
|
init: init,
|
||||||
|
refresh: refresh,
|
||||||
|
canThis: canThis,
|
||||||
|
actionsMap: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
}());
|
171
core/test/ghost/api_permissions_spec.js
Normal file
171
core/test/ghost/api_permissions_spec.js
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*globals describe, beforeEach, it*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var should = require('should'),
|
||||||
|
helpers = require('./helpers'),
|
||||||
|
errors = require('../../shared/errorHandling'),
|
||||||
|
Models = require('../../shared/models');
|
||||||
|
|
||||||
|
describe("Role Model", function () {
|
||||||
|
|
||||||
|
var RoleModel = Models.Role;
|
||||||
|
|
||||||
|
should.exist(RoleModel);
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
helpers.resetData().then(function() {
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can browse roles", function (done) {
|
||||||
|
RoleModel.browse().then(function (foundRoles) {
|
||||||
|
should.exist(foundRoles);
|
||||||
|
|
||||||
|
foundRoles.models.length.should.be.above(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can read roles", function (done) {
|
||||||
|
RoleModel.read({id: 1}).then(function (foundRole) {
|
||||||
|
should.exist(foundRole);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can edit roles", function (done) {
|
||||||
|
RoleModel.read({id: 1}).then(function (foundRole) {
|
||||||
|
should.exist(foundRole);
|
||||||
|
|
||||||
|
return foundRole.set({name: "updated"}).save();
|
||||||
|
}).then(function () {
|
||||||
|
return RoleModel.read({id: 1});
|
||||||
|
}).then(function (updatedRole) {
|
||||||
|
should.exist(updatedRole);
|
||||||
|
|
||||||
|
updatedRole.get("name").should.equal("updated");
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can add roles", function (done) {
|
||||||
|
var newRole = {
|
||||||
|
name: "test1",
|
||||||
|
description: "test1 description"
|
||||||
|
};
|
||||||
|
|
||||||
|
RoleModel.add(newRole).then(function (createdRole) {
|
||||||
|
should.exist(createdRole);
|
||||||
|
|
||||||
|
createdRole.attributes.name.should.equal(newRole.name);
|
||||||
|
createdRole.attributes.description.should.equal(newRole.description);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can delete roles", function (done) {
|
||||||
|
RoleModel.read({id: 1}).then(function (foundRole) {
|
||||||
|
should.exist(foundRole);
|
||||||
|
|
||||||
|
return RoleModel['delete'](1);
|
||||||
|
}).then(function () {
|
||||||
|
return RoleModel.browse();
|
||||||
|
}).then(function (foundRoles) {
|
||||||
|
var hasRemovedId = foundRoles.any(function(role) {
|
||||||
|
return role.id === 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
hasRemovedId.should.equal(false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Permission Model", function () {
|
||||||
|
|
||||||
|
var PermissionModel = Models.Permission;
|
||||||
|
|
||||||
|
should.exist(PermissionModel);
|
||||||
|
|
||||||
|
beforeEach(function(done) {
|
||||||
|
helpers.resetData().then(function() {
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can browse permissions", function (done) {
|
||||||
|
PermissionModel.browse().then(function (foundPermissions) {
|
||||||
|
should.exist(foundPermissions);
|
||||||
|
|
||||||
|
foundPermissions.models.length.should.be.above(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can read permissions", function (done) {
|
||||||
|
PermissionModel.read({id: 1}).then(function (foundPermission) {
|
||||||
|
should.exist(foundPermission);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can edit permissions", function (done) {
|
||||||
|
PermissionModel.read({id: 1}).then(function (foundPermission) {
|
||||||
|
should.exist(foundPermission);
|
||||||
|
|
||||||
|
return foundPermission.set({name: "updated"}).save();
|
||||||
|
}).then(function () {
|
||||||
|
return PermissionModel.read({id: 1});
|
||||||
|
}).then(function (updatedPermission) {
|
||||||
|
should.exist(updatedPermission);
|
||||||
|
|
||||||
|
updatedPermission.get("name").should.equal("updated");
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can add permissions", function (done) {
|
||||||
|
var newPerm = {
|
||||||
|
name: "testperm1"
|
||||||
|
};
|
||||||
|
|
||||||
|
PermissionModel.add(newPerm).then(function (createdPerm) {
|
||||||
|
should.exist(createdPerm);
|
||||||
|
|
||||||
|
createdPerm.attributes.name.should.equal(newPerm.name);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, done);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("can delete permissions", function (done) {
|
||||||
|
PermissionModel.read({id: 1}).then(function (foundPermission) {
|
||||||
|
should.exist(foundPermission);
|
||||||
|
|
||||||
|
return PermissionModel['delete'](1);
|
||||||
|
}).then(function () {
|
||||||
|
return PermissionModel.browse();
|
||||||
|
}).then(function (foundPermissions) {
|
||||||
|
var hasRemovedId = foundPermissions.any(function(permission) {
|
||||||
|
return permission.id === 1;
|
||||||
|
});
|
||||||
|
|
||||||
|
hasRemovedId.should.equal(false);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
|
@ -8,7 +8,7 @@
|
||||||
helpers = require('./helpers'),
|
helpers = require('./helpers'),
|
||||||
Models = require('../../shared/models');
|
Models = require('../../shared/models');
|
||||||
|
|
||||||
describe('Bookshelf Post Model', function () {
|
describe('Post Model', function () {
|
||||||
|
|
||||||
var PostModel = Models.Post;
|
var PostModel = Models.Post;
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
helpers = require('./helpers'),
|
helpers = require('./helpers'),
|
||||||
Models = require('../../shared/models');
|
Models = require('../../shared/models');
|
||||||
|
|
||||||
describe('Bookshelf Setting Model', function () {
|
describe('Setting Model', function () {
|
||||||
|
|
||||||
var SettingModel = Models.Setting;
|
var SettingModel = Models.Setting;
|
||||||
|
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
var _ = require('underscore'),
|
var _ = require('underscore'),
|
||||||
should = require('should'),
|
should = require('should'),
|
||||||
helpers = require('./helpers'),
|
helpers = require('./helpers'),
|
||||||
|
errors = require('../../shared/errorHandling'),
|
||||||
Models = require('../../shared/models');
|
Models = require('../../shared/models');
|
||||||
|
|
||||||
describe('Bookshelf User Model', function () {
|
describe('User Model', function () {
|
||||||
|
|
||||||
var UserModel = Models.User;
|
var UserModel = Models.User;
|
||||||
|
|
||||||
|
@ -134,6 +135,16 @@
|
||||||
|
|
||||||
}).then(null, done);
|
}).then(null, done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("can get effective permissions", function (done) {
|
||||||
|
UserModel.effectivePermissions(1).then(function (effectivePermissions) {
|
||||||
|
should.exist(effectivePermissions);
|
||||||
|
|
||||||
|
effectivePermissions.length.should.be.above(0);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.logError);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
}());
|
}());
|
|
@ -11,10 +11,12 @@
|
||||||
describe("Ghost API", function () {
|
describe("Ghost API", function () {
|
||||||
|
|
||||||
it("is a singleton", function () {
|
it("is a singleton", function () {
|
||||||
var ghost1 = new Ghost(),
|
var logStub = sinon.stub(console, "log"),
|
||||||
|
ghost1 = new Ghost(),
|
||||||
ghost2 = new Ghost();
|
ghost2 = new Ghost();
|
||||||
|
|
||||||
should.strictEqual(ghost1, ghost2);
|
should.strictEqual(ghost1, ghost2);
|
||||||
|
logStub.restore();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("uses init() to initialize", function (done) {
|
it("uses init() to initialize", function (done) {
|
||||||
|
|
226
core/test/ghost/permissions_spec.js
Normal file
226
core/test/ghost/permissions_spec.js
Normal file
|
@ -0,0 +1,226 @@
|
||||||
|
/*globals describe, beforeEach, it*/
|
||||||
|
|
||||||
|
(function () {
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
var _ = require("underscore"),
|
||||||
|
when = require('when'),
|
||||||
|
should = require('should'),
|
||||||
|
errors = require('../../shared/errorHandling'),
|
||||||
|
helpers = require('./helpers'),
|
||||||
|
permissions = require('../../shared/permissions'),
|
||||||
|
Models = require('../../shared/models'),
|
||||||
|
UserProvider = Models.User,
|
||||||
|
PermissionsProvider = Models.Permission;
|
||||||
|
|
||||||
|
describe('permissions', function () {
|
||||||
|
|
||||||
|
should.exist(permissions);
|
||||||
|
|
||||||
|
beforeEach(function (done) {
|
||||||
|
helpers.resetData().then(function () { done(); }, errors.throwError);
|
||||||
|
});
|
||||||
|
|
||||||
|
var testPerms = [
|
||||||
|
{ act: "edit", obj: "post" },
|
||||||
|
{ act: "edit", obj: "tag" },
|
||||||
|
{ act: "edit", obj: "user" },
|
||||||
|
{ act: "edit", obj: "page" },
|
||||||
|
{ act: "add", obj: "post" },
|
||||||
|
{ act: "add", obj: "user" },
|
||||||
|
{ act: "add", obj: "page" },
|
||||||
|
{ act: "remove", obj: "post" },
|
||||||
|
{ act: "remove", obj: "user" }
|
||||||
|
],
|
||||||
|
currTestPermId = 1,
|
||||||
|
createPermission = function (name, act, obj) {
|
||||||
|
if (!name) {
|
||||||
|
currTestPermId += 1;
|
||||||
|
name = "test" + currTestPermId;
|
||||||
|
}
|
||||||
|
|
||||||
|
var newPerm = {
|
||||||
|
name: name,
|
||||||
|
action_type: act,
|
||||||
|
object_type: obj
|
||||||
|
};
|
||||||
|
|
||||||
|
return PermissionsProvider.add(newPerm);
|
||||||
|
},
|
||||||
|
createTestPermissions = function() {
|
||||||
|
var createActions = _.map(testPerms, function (testPerm) {
|
||||||
|
return createPermission(null, testPerm.act, testPerm.obj);
|
||||||
|
});
|
||||||
|
|
||||||
|
return when.all(createActions);
|
||||||
|
};
|
||||||
|
|
||||||
|
it('can load an actions map from existing permissions', function (done) {
|
||||||
|
|
||||||
|
createTestPermissions()
|
||||||
|
.then(permissions.init)
|
||||||
|
.then(function (actionsMap) {
|
||||||
|
should.exist(actionsMap);
|
||||||
|
|
||||||
|
actionsMap.edit.should.eql(['post', 'tag', 'user', 'page']);
|
||||||
|
|
||||||
|
actionsMap.should.equal(permissions.actionsMap);
|
||||||
|
|
||||||
|
done();
|
||||||
|
}, errors.throwError);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add user to role', function (done) {
|
||||||
|
var existingUserRoles;
|
||||||
|
|
||||||
|
UserProvider.read({id: 1}, { withRelated: ['roles'] }).then(function (foundUser) {
|
||||||
|
var testRole = new Models.Role({
|
||||||
|
name: 'testrole1',
|
||||||
|
description: 'testrole1 description'
|
||||||
|
});
|
||||||
|
|
||||||
|
should.exist(foundUser);
|
||||||
|
|
||||||
|
should.exist(foundUser.roles());
|
||||||
|
|
||||||
|
existingUserRoles = foundUser.related('roles').length;
|
||||||
|
|
||||||
|
return testRole.save().then(function () {
|
||||||
|
return foundUser.roles().attach(testRole);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
return UserProvider.read({id: 1}, { withRelated: ['roles'] });
|
||||||
|
}).then(function (updatedUser) {
|
||||||
|
should.exist(updatedUser);
|
||||||
|
|
||||||
|
updatedUser.related('roles').length.should.equal(existingUserRoles + 1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add user permissions', function (done) {
|
||||||
|
Models.User.read({id: 1}, { withRelated: ['permissions']}).then(function (testUser) {
|
||||||
|
var testPermission = new Models.Permission({
|
||||||
|
name: "test edit posts",
|
||||||
|
action_type: 'edit',
|
||||||
|
object_type: 'post'
|
||||||
|
});
|
||||||
|
|
||||||
|
testUser.related('permissions').length.should.equal(0);
|
||||||
|
|
||||||
|
return testPermission.save().then(function () {
|
||||||
|
return testUser.permissions().attach(testPermission);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
return Models.User.read({id: 1}, { withRelated: ['permissions']});
|
||||||
|
}).then(function (updatedUser) {
|
||||||
|
should.exist(updatedUser);
|
||||||
|
|
||||||
|
updatedUser.related('permissions').length.should.equal(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('can add role permissions', function (done) {
|
||||||
|
var testRole = new Models.Role({
|
||||||
|
name: "test2",
|
||||||
|
description: "test2 description"
|
||||||
|
});
|
||||||
|
|
||||||
|
testRole.save().then(function () {
|
||||||
|
return testRole.load('permissions');
|
||||||
|
}).then(function () {
|
||||||
|
var rolePermission = new Models.Permission({
|
||||||
|
name: "test edit posts",
|
||||||
|
action_type: 'edit',
|
||||||
|
object_type: 'post'
|
||||||
|
});
|
||||||
|
|
||||||
|
testRole.related('permissions').length.should.equal(0);
|
||||||
|
|
||||||
|
return rolePermission.save().then(function () {
|
||||||
|
return testRole.permissions().attach(rolePermission);
|
||||||
|
});
|
||||||
|
}).then(function () {
|
||||||
|
return Models.Role.read({id: testRole.id}, { withRelated: ['permissions']});
|
||||||
|
}).then(function (updatedRole) {
|
||||||
|
should.exist(updatedRole);
|
||||||
|
|
||||||
|
updatedRole.related('permissions').length.should.equal(1);
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not allow edit post without permission', function (done) {
|
||||||
|
var fakePage = {
|
||||||
|
id: 1
|
||||||
|
};
|
||||||
|
|
||||||
|
createTestPermissions()
|
||||||
|
.then(permissions.init)
|
||||||
|
.then(function () {
|
||||||
|
return Models.User.read({id: 1});
|
||||||
|
})
|
||||||
|
.then(function (foundUser) {
|
||||||
|
var canThisResult = permissions.canThis(foundUser);
|
||||||
|
|
||||||
|
should.exist(canThisResult.edit);
|
||||||
|
should.exist(canThisResult.edit.post);
|
||||||
|
|
||||||
|
return canThisResult.edit.page(fakePage);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
errors.logError(new Error("Allowed edit post without permission"));
|
||||||
|
}, function () {
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('allows edit post with permission', function (done) {
|
||||||
|
var fakePost = {
|
||||||
|
id: "1"
|
||||||
|
};
|
||||||
|
|
||||||
|
createTestPermissions()
|
||||||
|
.then(permissions.init)
|
||||||
|
.then(function () {
|
||||||
|
return Models.User.read({id: 1});
|
||||||
|
})
|
||||||
|
.then(function (foundUser) {
|
||||||
|
var newPerm = new Models.Permission({
|
||||||
|
name: "test3 edit post",
|
||||||
|
action_type: "edit",
|
||||||
|
object_type: "post"
|
||||||
|
});
|
||||||
|
|
||||||
|
return newPerm.save().then(function () {
|
||||||
|
return foundUser.permissions().attach(newPerm);
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
return Models.User.read({id: 1}, { withRelated: ['permissions']});
|
||||||
|
})
|
||||||
|
.then(function (updatedUser) {
|
||||||
|
|
||||||
|
// TODO: Verify updatedUser.related('permissions') has the permission?
|
||||||
|
|
||||||
|
var canThisResult = permissions.canThis(updatedUser);
|
||||||
|
|
||||||
|
should.exist(canThisResult.edit);
|
||||||
|
should.exist(canThisResult.edit.post);
|
||||||
|
|
||||||
|
return canThisResult.edit.post(fakePost);
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
done();
|
||||||
|
}, function () {
|
||||||
|
errors.logError(new Error("Did not allow edit post with permission"));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}());
|
Loading…
Add table
Reference in a new issue