mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-24 06:35:49 +03:00
Merge pull request #419 from jgable/postPermissions
Edit Post Permissions
This commit is contained in:
commit
fd33b276a0
@ -54,13 +54,14 @@
|
|||||||
// by `addSubview`, which will in-turn remove any
|
// by `addSubview`, which will in-turn remove any
|
||||||
// children of those views, and so on.
|
// children of those views, and so on.
|
||||||
removeSubviews: function () {
|
removeSubviews: function () {
|
||||||
var i, l, children = this.subviews;
|
var children = this.subviews;
|
||||||
|
|
||||||
if (!children) {
|
if (!children) {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
for (i = 0, l = children.length; i < l; i += 1) {
|
|
||||||
children[i].remove();
|
_(children).invoke("remove");
|
||||||
}
|
|
||||||
this.subviews = [];
|
this.subviews = [];
|
||||||
return this;
|
return this;
|
||||||
},
|
},
|
||||||
@ -72,6 +73,32 @@
|
|||||||
this.removeSubviews();
|
this.removeSubviews();
|
||||||
}
|
}
|
||||||
return Backbone.View.prototype.remove.apply(this, arguments);
|
return Backbone.View.prototype.remove.apply(this, arguments);
|
||||||
|
},
|
||||||
|
|
||||||
|
// Used in API request fail handlers to parse a standard api error
|
||||||
|
// response json for the message to display
|
||||||
|
getRequestErrorMessage: function (request) {
|
||||||
|
var message;
|
||||||
|
|
||||||
|
// Can't really continue without a request
|
||||||
|
if (!request) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Seems like a sensible default
|
||||||
|
message = request.statusText;
|
||||||
|
|
||||||
|
// If a non 200 response
|
||||||
|
if (request.status !== 200) {
|
||||||
|
try {
|
||||||
|
// Try to parse out the error, or default to "Unknown"
|
||||||
|
message = request.responseJSON.error || "Unknown Error";
|
||||||
|
} catch (e) {
|
||||||
|
message = "The server returned an error (" + (request.status || "?") + ").";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -157,17 +157,20 @@
|
|||||||
if (e) {
|
if (e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
}
|
}
|
||||||
var model = this.model;
|
var view = this,
|
||||||
|
model = this.model;
|
||||||
this.savePost().then(function () {
|
this.savePost().then(function () {
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'success',
|
type: 'success',
|
||||||
message: 'Your post was saved as ' + model.get('status'),
|
message: 'Your post was saved as ' + model.get('status'),
|
||||||
status: 'passive'
|
status: 'passive'
|
||||||
});
|
});
|
||||||
}, function () {
|
}, function (request) {
|
||||||
|
var message = view.getRequestErrorMessage(request) || model.validationError;
|
||||||
|
|
||||||
Ghost.notifications.addItem({
|
Ghost.notifications.addItem({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
message: model.validationError,
|
message: message,
|
||||||
status: 'passive'
|
status: 'passive'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -15,6 +15,7 @@ var config = require('./../config'),
|
|||||||
models = require('./server/models'),
|
models = require('./server/models'),
|
||||||
plugins = require('./server/plugins'),
|
plugins = require('./server/plugins'),
|
||||||
requireTree = require('./server/require-tree'),
|
requireTree = require('./server/require-tree'),
|
||||||
|
permissions = require('./server/permissions'),
|
||||||
|
|
||||||
// Variables
|
// Variables
|
||||||
appRoot = path.resolve(__dirname, '../'),
|
appRoot = path.resolve(__dirname, '../'),
|
||||||
@ -124,9 +125,14 @@ Ghost.prototype.init = function () {
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
|
return when.join(instance.dataProvider.init(), instance.getPaths()).then(function () {
|
||||||
|
// Initialize plugins
|
||||||
return self.initPlugins();
|
return self.initPlugins();
|
||||||
}, errors.logAndThrowError).then(function () {
|
}).then(function () {
|
||||||
|
// Initialize the settings cache
|
||||||
return self.updateSettingsCache();
|
return self.updateSettingsCache();
|
||||||
|
}).then(function () {
|
||||||
|
// Initialize the permissions actions and objects
|
||||||
|
return permissions.init();
|
||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@ var Ghost = require('../ghost'),
|
|||||||
_ = require('underscore'),
|
_ = require('underscore'),
|
||||||
when = require('when'),
|
when = require('when'),
|
||||||
errors = require('./errorHandling'),
|
errors = require('./errorHandling'),
|
||||||
|
permissions = require('./permissions'),
|
||||||
|
canThis = permissions.canThis,
|
||||||
|
|
||||||
ghost = new Ghost(),
|
ghost = new Ghost(),
|
||||||
dataProvider = ghost.dataProvider,
|
dataProvider = ghost.dataProvider,
|
||||||
@ -40,7 +42,15 @@ posts = {
|
|||||||
// **takes:** a json object with all the properties which should be updated
|
// **takes:** a json object with all the properties which should be updated
|
||||||
edit: function edit(postData) {
|
edit: function edit(postData) {
|
||||||
// **returns:** a promise for the resulting post in a json object
|
// **returns:** a promise for the resulting post in a json object
|
||||||
return dataProvider.Post.edit(postData);
|
if (!this.user) {
|
||||||
|
return when.reject("You do not have permission to edit this post.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return canThis(this.user).edit.post(postData.id).then(function () {
|
||||||
|
return dataProvider.Post.edit(postData);
|
||||||
|
}, function () {
|
||||||
|
return when.reject("You do not have permission to edit this post.");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// #### Add
|
// #### Add
|
||||||
@ -48,7 +58,15 @@ posts = {
|
|||||||
// **takes:** a json object representing a post,
|
// **takes:** a json object representing a post,
|
||||||
add: function add(postData) {
|
add: function add(postData) {
|
||||||
// **returns:** a promise for the resulting post in a json object
|
// **returns:** a promise for the resulting post in a json object
|
||||||
return dataProvider.Post.add(postData);
|
if (!this.user) {
|
||||||
|
return when.reject("You do not have permission to add posts.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return canThis(this.user).create.post().then(function () {
|
||||||
|
return dataProvider.Post.add(postData);
|
||||||
|
}, function () {
|
||||||
|
return when.reject("You do not have permission to add posts.");
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
// #### Destroy
|
// #### Destroy
|
||||||
@ -56,7 +74,15 @@ posts = {
|
|||||||
// **takes:** an identifier (id or slug?)
|
// **takes:** an identifier (id or slug?)
|
||||||
destroy: function destroy(args) {
|
destroy: function destroy(args) {
|
||||||
// **returns:** a promise for a json response with the id of the deleted post
|
// **returns:** a promise for a json response with the id of the deleted post
|
||||||
return dataProvider.Post.destroy(args.id);
|
if (!this.user) {
|
||||||
|
return when.reject("You do not have permission to remove posts.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return canThis(this.user).remove.post(args.id).then(function () {
|
||||||
|
return dataProvider.Post.destroy(args.id);
|
||||||
|
}, function () {
|
||||||
|
return when.reject("You do not have permission to remove posts.");
|
||||||
|
});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -231,20 +231,30 @@ Post = GhostBookshelf.Model.extend({
|
|||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This logic is temporary, will probably need to be updated
|
// Check if any permissions apply for this user and post.
|
||||||
|
|
||||||
hasPermission = _.any(userPermissions, function (perm) {
|
hasPermission = _.any(userPermissions, function (perm) {
|
||||||
if (perm.get('object_type') !== 'post') {
|
// Check for matching action type and object type
|
||||||
|
if (perm.get('action_type') !== action_type ||
|
||||||
|
perm.get('object_type') !== 'post') {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// True, if no object_id specified, or it matches
|
// If asking whether we can create posts,
|
||||||
|
// and we have a create posts permission then go ahead and say yes
|
||||||
|
if (action_type === 'create' && perm.get('action_type') === action_type) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for either no object id or a matching one
|
||||||
return !perm.get('object_id') || perm.get('object_id') === postModel.id;
|
return !perm.get('object_id') || perm.get('object_id') === postModel.id;
|
||||||
});
|
});
|
||||||
|
|
||||||
// If this is the author of the post, allow it.
|
// If this is the author of the post, allow it.
|
||||||
hasPermission = hasPermission || userId === postModel.get('author_id');
|
// Moved below the permissions checks because there may not be a postModel
|
||||||
|
// in the case like canThis(user).create.post()
|
||||||
|
hasPermission = hasPermission || (postModel && userId === postModel.get('author_id'));
|
||||||
|
|
||||||
|
// Resolve if we have appropriate permissions
|
||||||
if (hasPermission) {
|
if (hasPermission) {
|
||||||
return when.resolve();
|
return when.resolve();
|
||||||
}
|
}
|
||||||
|
@ -54,15 +54,9 @@ User = GhostBookshelf.Model.extend({
|
|||||||
*/
|
*/
|
||||||
add: function (_user) {
|
add: function (_user) {
|
||||||
|
|
||||||
var User = this,
|
var self = this,
|
||||||
// Clone the _user so we don't expose the hashed password unnecessarily
|
// Clone the _user so we don't expose the hashed password unnecessarily
|
||||||
userData = _.extend({}, _user),
|
userData = _.extend({}, _user);
|
||||||
fail = false,
|
|
||||||
userRoles = {
|
|
||||||
|
|
||||||
"role_id": 1,
|
|
||||||
"user_id": 1
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This only allows one user to be added to the database, otherwise fails.
|
* This only allows one user to be added to the database, otherwise fails.
|
||||||
@ -70,20 +64,27 @@ User = GhostBookshelf.Model.extend({
|
|||||||
* @author javorszky
|
* @author javorszky
|
||||||
*/
|
*/
|
||||||
return this.forge().fetch().then(function (user) {
|
return this.forge().fetch().then(function (user) {
|
||||||
|
// Check if user exists
|
||||||
if (user) {
|
if (user) {
|
||||||
fail = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (fail) {
|
|
||||||
return when.reject(new Error('A user is already registered. Only one user for now!'));
|
return when.reject(new Error('A user is already registered. Only one user for now!'));
|
||||||
}
|
}
|
||||||
|
|
||||||
return nodefn.call(bcrypt.hash, _user.password, null, null).then(function (hash) {
|
// Hash the provided password with bcrypt
|
||||||
userData.password = hash;
|
return nodefn.call(bcrypt.hash, _user.password, null, null);
|
||||||
GhostBookshelf.Model.add.call(UserRole, userRoles);
|
}).then(function (hash) {
|
||||||
return GhostBookshelf.Model.add.call(User, userData);
|
// Assign the hashed password
|
||||||
}, errors.logAndThrowError);
|
userData.password = hash;
|
||||||
|
// Save the user with the hashed password
|
||||||
|
return GhostBookshelf.Model.add.call(self, userData);
|
||||||
|
}).then(function (addedUser) {
|
||||||
|
// Assign the userData to our created user so we can pass it back
|
||||||
|
userData = addedUser;
|
||||||
|
|
||||||
|
// Add this user to the admin role (assumes admin = role_id: 1)
|
||||||
|
return UserRole.add({role_id: 1, user_id: addedUser.id});
|
||||||
|
}).then(function (addedUserRole) {
|
||||||
|
// Return the added user as expected
|
||||||
|
return when.resolve(userData);
|
||||||
}, errors.logAndThrowError);
|
}, errors.logAndThrowError);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -13,6 +13,14 @@ var _ = require('underscore'),
|
|||||||
CanThisResult,
|
CanThisResult,
|
||||||
exported;
|
exported;
|
||||||
|
|
||||||
|
function hasActionsMap() {
|
||||||
|
// Just need to find one key in the actionsMap
|
||||||
|
|
||||||
|
return _.any(exported.actionsMap, function (val, key) {
|
||||||
|
return Object.hasOwnProperty(key);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Base class for canThis call results
|
// Base class for canThis call results
|
||||||
CanThisResult = function () {
|
CanThisResult = function () {
|
||||||
this.userPermissionLoad = false;
|
this.userPermissionLoad = false;
|
||||||
@ -98,14 +106,16 @@ CanThisResult.prototype.beginCheck = function (user) {
|
|||||||
var self = this,
|
var self = this,
|
||||||
userId = user.id || user;
|
userId = user.id || user;
|
||||||
|
|
||||||
|
if (!hasActionsMap()) {
|
||||||
|
throw new Error("No actions map found, please call permissions.init() before use.");
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: Switch logic based on object type; user, role, post.
|
// TODO: Switch logic based on object type; user, role, post.
|
||||||
|
|
||||||
// Kick off the fetching of the user data
|
// Kick off the fetching of the user data
|
||||||
this.userPermissionLoad = UserProvider.effectivePermissions(userId);
|
this.userPermissionLoad = UserProvider.effectivePermissions(userId);
|
||||||
|
|
||||||
// Iterate through the actions and their related object types
|
// 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) {
|
_.each(exported.actionsMap, function (obj_types, act_type) {
|
||||||
// Build up the object type handlers;
|
// Build up the object type handlers;
|
||||||
// the '.post()' parts in canThis(user).edit.post()
|
// the '.post()' parts in canThis(user).edit.post()
|
||||||
|
@ -224,7 +224,7 @@ describe('permissions', function () {
|
|||||||
|
|
||||||
// TODO: Verify updatedUser.related('permissions') has the permission?
|
// TODO: Verify updatedUser.related('permissions') has the permission?
|
||||||
|
|
||||||
var canThisResult = permissions.canThis(updatedUser);
|
var canThisResult = permissions.canThis(updatedUser.id);
|
||||||
|
|
||||||
should.exist(canThisResult.edit);
|
should.exist(canThisResult.edit);
|
||||||
should.exist(canThisResult.edit.post);
|
should.exist(canThisResult.edit.post);
|
||||||
|
Loading…
Reference in New Issue
Block a user