2
1
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2023-12-13 21:00:40 +01:00

add small permission improvements

no issue
- do not check client type in auth middleware
- offer filtering for findAll function in base
- add isInternalContext to base model
This commit is contained in:
Katharina Irrgang 2016-04-14 17:54:49 +02:00 committed by kirrg001
parent 2744fed5d8
commit f644d99460
10 changed files with 47 additions and 17 deletions

View file

@ -113,7 +113,7 @@ db = {
*/
deleteAllContent: function (options) {
var tasks,
queryOpts = {columns: 'id'};
queryOpts = {columns: 'id', context: {internal: true}};
options = options || {};

View file

@ -65,7 +65,7 @@ auth = {
delete req.body.client_id;
delete req.body.client_secret;
if (!client || client.type !== 'ua') {
if (!client) {
errors.logError(
i18n.t('errors.middleware.auth.clientAuthenticationFailed'),
i18n.t('errors.middleware.auth.clientCredentialsNotValid'),

View file

@ -233,14 +233,24 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
/**
* ### Find All
* Naive find all fetches all the data for a particular model
* Fetches all the data for a particular model
* @param {Object} options (optional)
* @return {Promise(ghostBookshelf.Collection)} Collection of all Models
*/
findAll: function findAll(options) {
options = this.filterOptions(options, 'findAll');
options.withRelated = _.union(options.withRelated, options.include);
return this.forge().fetchAll(options).then(function then(result) {
var itemCollection = this.forge(null, {context: options.context});
// transforms fictive keywords like 'all' (status:all) into correct allowed values
if (this.processOptions) {
this.processOptions(options);
}
itemCollection.applyDefaultAndCustomFilters(options);
return itemCollection.fetchAll(options).then(function then(result) {
if (options.include) {
_.each(result.models, function each(item) {
item.include = options.include;
@ -290,7 +300,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
this.processOptions(options);
// Add Filter behaviour
itemCollection.applyFilters(options);
itemCollection.applyDefaultAndCustomFilters(options);
// Handle related objects
// TODO: this should just be done for all methods @ the API level
@ -306,7 +316,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
} else {
options.order = self.orderDefaultOptions();
}
return itemCollection.fetchPage(options).then(function formatResponse(response) {
var data = {};

View file

@ -11,6 +11,7 @@ module.exports = function (Bookshelf) {
* Cached copy of the context setup for this model instance
*/
_context: null,
/**
* ## Is Public Context?
* A helper to determine if this is a public request or not
@ -18,6 +19,10 @@ module.exports = function (Bookshelf) {
*/
isPublicContext: function isPublicContext() {
return !!(this._context && this._context.public);
},
isInternalContext: function isInternalContext() {
return !!(this._context && this._context.internal);
}
},
{

View file

@ -118,6 +118,7 @@ filter = function filter(Bookshelf) {
.query('join', 'users as author', 'author.id', '=', 'posts.author_id');
}
},
/**
* ## fetchAndCombineFilters
* Helper method, uses the combineFilters util to apply filters to the current model instance
@ -137,6 +138,7 @@ filter = function filter(Bookshelf) {
return this;
},
/**
* ## Apply Filters
* Method which makes the necessary query builder calls (through knex) for the filters set
@ -144,7 +146,7 @@ filter = function filter(Bookshelf) {
* @param {Object} options
* @returns {Bookshelf.Model}
*/
applyFilters: function applyFilters(options) {
applyDefaultAndCustomFilters: function applyDefaultAndCustomFilters(options) {
var self = this;
// @TODO figure out a better place/way to trigger loading filters

View file

@ -394,6 +394,10 @@ Post = ghostBookshelf.Model.extend({
return this.isPublicContext() ? 'status:published' : null;
},
defaultFilters: function defaultFilters() {
if (this.isInternalContext()) {
return null;
}
return this.isPublicContext() ? 'page:false' : 'page:false+status:published';
}
}, {
@ -458,7 +462,7 @@ Post = ghostBookshelf.Model.extend({
validOptions = {
findOne: ['columns', 'importing', 'withRelated', 'require'],
findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status', 'staticPages'],
findAll: ['columns'],
findAll: ['columns', 'filter'],
add: ['importing']
};
@ -636,6 +640,7 @@ Post = ghostBookshelf.Model.extend({
if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) {
// Grab the original args without the first one
origArgs = _.toArray(arguments).slice(1);
// Get the actual post model
return this.findOne({id: postModelOrId, status: 'all'}).then(function then(foundPostModel) {
// Build up the original args but substitute with actual model

View file

@ -170,9 +170,15 @@ User = ghostBookshelf.Model.extend({
return role.get('name') === roleName;
});
},
enforcedFilters: function enforcedFilters() {
if (this.isInternalContext()) {
return null;
}
return this.isPublicContext() ? 'status:[' + activeStates.join(',') + ']' : null;
},
defaultFilters: function defaultFilters() {
return this.isPublicContext() ? null : 'status:[' + activeStates.join(',') + ']';
}
@ -235,7 +241,8 @@ User = ghostBookshelf.Model.extend({
findOne: ['withRelated', 'status'],
setup: ['id'],
edit: ['withRelated', 'id'],
findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status']
findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status'],
findAll: ['filter']
};
if (validOptions[methodName]) {

View file

@ -139,12 +139,13 @@ describe('Database Migration (special functions)', function () {
describe('Populate', function () {
beforeEach(testUtils.setup());
it('should populate all fixtures correctly', function (done) {
fixtures.populate(loggerStub).then(function () {
var props = {
posts: Models.Post.findAll({include: ['tags']}),
tags: Models.Tag.findAll(),
users: Models.User.findAll({include: ['roles']}),
users: Models.User.findAll({filter: 'status:inactive', context: {internal:true}, include: ['roles']}),
clients: Models.Client.findAll(),
roles: Models.Role.findAll(),
permissions: Models.Permission.findAll({include: ['roles']})
@ -181,6 +182,7 @@ describe('Database Migration (special functions)', function () {
should.exist(result.users);
result.users.length.should.eql(1);
result.users.at(0).get('name').should.eql('Ghost Owner');
result.users.at(0).get('status').should.eql('inactive');
result.users.at(0).related('roles').length.should.eql(1);
result.users.at(0).related('roles').at(0).get('name').should.eql('Owner');

View file

@ -1324,14 +1324,14 @@ describe('Post Model', function () {
// We're going to delete all posts by user 1
var authorData = {id: 1};
PostModel.findAll().then(function (found) {
PostModel.findAll({context:{internal:true}}).then(function (found) {
// There are 50 posts to begin with
found.length.should.equal(50);
return PostModel.destroyByAuthor(authorData);
}).then(function (results) {
// User 1 has 13 posts in the database
results.length.should.equal(13);
return PostModel.findAll();
return PostModel.findAll({context:{internal:true}});
}).then(function (found) {
// Only 37 should remain
found.length.should.equal(37);

View file

@ -153,7 +153,7 @@ describe('Filter', function () {
});
});
describe('Apply Filters', function () {
describe('Apply Default and Custom Filters', function () {
var fetchSpy,
restoreGQL,
filterGQL;
@ -174,7 +174,7 @@ describe('Filter', function () {
});
it('should call fetchAndCombineFilters if _filters not set', function () {
var result = ghostBookshelf.Model.prototype.applyFilters();
var result = ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
fetchSpy.calledOnce.should.be.true();
should(result._filters).be.null();
@ -183,7 +183,7 @@ describe('Filter', function () {
it('should NOT call fetchAndCombineFilters if _filters IS set', function () {
ghostBookshelf.Model.prototype._filters = 'test';
var result = ghostBookshelf.Model.prototype.applyFilters();
var result = ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
fetchSpy.called.should.be.false();
result._filters.should.eql('test');
@ -193,7 +193,7 @@ describe('Filter', function () {
ghostBookshelf.Model.prototype._filters = {statements: [
{prop: 'title', op: '=', value: 'Hello Word'}
]};
ghostBookshelf.Model.prototype.applyFilters();
ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
fetchSpy.called.should.be.false();
filterGQL.knexify.called.should.be.true();
@ -208,7 +208,7 @@ describe('Filter', function () {
{prop: 'tags', op: 'IN', value: ['photo', 'video']}
]};
ghostBookshelf.Model.prototype.applyFilters();
ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
filterGQL.json.printStatements.calledOnce.should.be.true();
filterGQL.json.printStatements.firstCall.args[0].should.eql([
{prop: 'tags', op: 'IN', value: ['photo', 'video']}