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) { deleteAllContent: function (options) {
var tasks, var tasks,
queryOpts = {columns: 'id'}; queryOpts = {columns: 'id', context: {internal: true}};
options = options || {}; options = options || {};

View File

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

View File

@ -233,14 +233,24 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
/** /**
* ### Find All * ### 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) * @param {Object} options (optional)
* @return {Promise(ghostBookshelf.Collection)} Collection of all Models * @return {Promise(ghostBookshelf.Collection)} Collection of all Models
*/ */
findAll: function findAll(options) { findAll: function findAll(options) {
options = this.filterOptions(options, 'findAll'); options = this.filterOptions(options, 'findAll');
options.withRelated = _.union(options.withRelated, options.include); 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) { if (options.include) {
_.each(result.models, function each(item) { _.each(result.models, function each(item) {
item.include = options.include; item.include = options.include;
@ -290,7 +300,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
this.processOptions(options); this.processOptions(options);
// Add Filter behaviour // Add Filter behaviour
itemCollection.applyFilters(options); itemCollection.applyDefaultAndCustomFilters(options);
// Handle related objects // Handle related objects
// TODO: this should just be done for all methods @ the API level // TODO: this should just be done for all methods @ the API level
@ -306,7 +316,6 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
} else { } else {
options.order = self.orderDefaultOptions(); options.order = self.orderDefaultOptions();
} }
return itemCollection.fetchPage(options).then(function formatResponse(response) { return itemCollection.fetchPage(options).then(function formatResponse(response) {
var data = {}; var data = {};

View File

@ -11,6 +11,7 @@ module.exports = function (Bookshelf) {
* Cached copy of the context setup for this model instance * Cached copy of the context setup for this model instance
*/ */
_context: null, _context: null,
/** /**
* ## Is Public Context? * ## Is Public Context?
* A helper to determine if this is a public request or not * A helper to determine if this is a public request or not
@ -18,6 +19,10 @@ module.exports = function (Bookshelf) {
*/ */
isPublicContext: function isPublicContext() { isPublicContext: function isPublicContext() {
return !!(this._context && this._context.public); 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'); .query('join', 'users as author', 'author.id', '=', 'posts.author_id');
} }
}, },
/** /**
* ## fetchAndCombineFilters * ## fetchAndCombineFilters
* Helper method, uses the combineFilters util to apply filters to the current model instance * Helper method, uses the combineFilters util to apply filters to the current model instance
@ -137,6 +138,7 @@ filter = function filter(Bookshelf) {
return this; return this;
}, },
/** /**
* ## Apply Filters * ## Apply Filters
* Method which makes the necessary query builder calls (through knex) for the filters set * 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 * @param {Object} options
* @returns {Bookshelf.Model} * @returns {Bookshelf.Model}
*/ */
applyFilters: function applyFilters(options) { applyDefaultAndCustomFilters: function applyDefaultAndCustomFilters(options) {
var self = this; var self = this;
// @TODO figure out a better place/way to trigger loading filters // @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; return this.isPublicContext() ? 'status:published' : null;
}, },
defaultFilters: function defaultFilters() { defaultFilters: function defaultFilters() {
if (this.isInternalContext()) {
return null;
}
return this.isPublicContext() ? 'page:false' : 'page:false+status:published'; return this.isPublicContext() ? 'page:false' : 'page:false+status:published';
} }
}, { }, {
@ -458,7 +462,7 @@ Post = ghostBookshelf.Model.extend({
validOptions = { validOptions = {
findOne: ['columns', 'importing', 'withRelated', 'require'], findOne: ['columns', 'importing', 'withRelated', 'require'],
findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status', 'staticPages'], findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status', 'staticPages'],
findAll: ['columns'], findAll: ['columns', 'filter'],
add: ['importing'] add: ['importing']
}; };
@ -636,6 +640,7 @@ Post = ghostBookshelf.Model.extend({
if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) { if (_.isNumber(postModelOrId) || _.isString(postModelOrId)) {
// Grab the original args without the first one // Grab the original args without the first one
origArgs = _.toArray(arguments).slice(1); origArgs = _.toArray(arguments).slice(1);
// Get the actual post model // Get the actual post model
return this.findOne({id: postModelOrId, status: 'all'}).then(function then(foundPostModel) { return this.findOne({id: postModelOrId, status: 'all'}).then(function then(foundPostModel) {
// Build up the original args but substitute with actual model // 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; return role.get('name') === roleName;
}); });
}, },
enforcedFilters: function enforcedFilters() { enforcedFilters: function enforcedFilters() {
if (this.isInternalContext()) {
return null;
}
return this.isPublicContext() ? 'status:[' + activeStates.join(',') + ']' : null; return this.isPublicContext() ? 'status:[' + activeStates.join(',') + ']' : null;
}, },
defaultFilters: function defaultFilters() { defaultFilters: function defaultFilters() {
return this.isPublicContext() ? null : 'status:[' + activeStates.join(',') + ']'; return this.isPublicContext() ? null : 'status:[' + activeStates.join(',') + ']';
} }
@ -235,7 +241,8 @@ User = ghostBookshelf.Model.extend({
findOne: ['withRelated', 'status'], findOne: ['withRelated', 'status'],
setup: ['id'], setup: ['id'],
edit: ['withRelated', 'id'], edit: ['withRelated', 'id'],
findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status'] findPage: ['page', 'limit', 'columns', 'filter', 'order', 'status'],
findAll: ['filter']
}; };
if (validOptions[methodName]) { if (validOptions[methodName]) {

View File

@ -139,12 +139,13 @@ describe('Database Migration (special functions)', function () {
describe('Populate', function () { describe('Populate', function () {
beforeEach(testUtils.setup()); beforeEach(testUtils.setup());
it('should populate all fixtures correctly', function (done) { it('should populate all fixtures correctly', function (done) {
fixtures.populate(loggerStub).then(function () { fixtures.populate(loggerStub).then(function () {
var props = { var props = {
posts: Models.Post.findAll({include: ['tags']}), posts: Models.Post.findAll({include: ['tags']}),
tags: Models.Tag.findAll(), 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(), clients: Models.Client.findAll(),
roles: Models.Role.findAll(), roles: Models.Role.findAll(),
permissions: Models.Permission.findAll({include: ['roles']}) permissions: Models.Permission.findAll({include: ['roles']})
@ -181,6 +182,7 @@ describe('Database Migration (special functions)', function () {
should.exist(result.users); should.exist(result.users);
result.users.length.should.eql(1); result.users.length.should.eql(1);
result.users.at(0).get('name').should.eql('Ghost Owner'); 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').length.should.eql(1);
result.users.at(0).related('roles').at(0).get('name').should.eql('Owner'); 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 // We're going to delete all posts by user 1
var authorData = {id: 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 // There are 50 posts to begin with
found.length.should.equal(50); found.length.should.equal(50);
return PostModel.destroyByAuthor(authorData); return PostModel.destroyByAuthor(authorData);
}).then(function (results) { }).then(function (results) {
// User 1 has 13 posts in the database // User 1 has 13 posts in the database
results.length.should.equal(13); results.length.should.equal(13);
return PostModel.findAll(); return PostModel.findAll({context:{internal:true}});
}).then(function (found) { }).then(function (found) {
// Only 37 should remain // Only 37 should remain
found.length.should.equal(37); 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, var fetchSpy,
restoreGQL, restoreGQL,
filterGQL; filterGQL;
@ -174,7 +174,7 @@ describe('Filter', function () {
}); });
it('should call fetchAndCombineFilters if _filters not set', 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(); fetchSpy.calledOnce.should.be.true();
should(result._filters).be.null(); should(result._filters).be.null();
@ -183,7 +183,7 @@ describe('Filter', function () {
it('should NOT call fetchAndCombineFilters if _filters IS set', function () { it('should NOT call fetchAndCombineFilters if _filters IS set', function () {
ghostBookshelf.Model.prototype._filters = 'test'; ghostBookshelf.Model.prototype._filters = 'test';
var result = ghostBookshelf.Model.prototype.applyFilters(); var result = ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
fetchSpy.called.should.be.false(); fetchSpy.called.should.be.false();
result._filters.should.eql('test'); result._filters.should.eql('test');
@ -193,7 +193,7 @@ describe('Filter', function () {
ghostBookshelf.Model.prototype._filters = {statements: [ ghostBookshelf.Model.prototype._filters = {statements: [
{prop: 'title', op: '=', value: 'Hello Word'} {prop: 'title', op: '=', value: 'Hello Word'}
]}; ]};
ghostBookshelf.Model.prototype.applyFilters(); ghostBookshelf.Model.prototype.applyDefaultAndCustomFilters();
fetchSpy.called.should.be.false(); fetchSpy.called.should.be.false();
filterGQL.knexify.called.should.be.true(); filterGQL.knexify.called.should.be.true();
@ -208,7 +208,7 @@ describe('Filter', function () {
{prop: 'tags', op: 'IN', value: ['photo', 'video']} {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.calledOnce.should.be.true();
filterGQL.json.printStatements.firstCall.args[0].should.eql([ filterGQL.json.printStatements.firstCall.args[0].should.eql([
{prop: 'tags', op: 'IN', value: ['photo', 'video']} {prop: 'tags', op: 'IN', value: ['photo', 'video']}