mirror of
https://github.com/TryGhost/Ghost.git
synced 2025-04-08 02:52:39 -05:00
🎨 User is not allowed to add/modify certain fields (#9053)
no issue - it's not allowed to change/add these attributes via the API - created_at = is only once set on adding the resource - created_by = is only once set on adding the resource - updated_by = is set on the server side when updating the model (based on who is logged in) - updated_at = is set on the server side when updating the model * Revert the usage of the access rules plugin
This commit is contained in:
parent
d3d04a8e72
commit
42af268d1b
3 changed files with 184 additions and 4 deletions
|
@ -125,16 +125,29 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
return validation.validateSchema(this.tableName, this.toJSON());
|
||||
},
|
||||
|
||||
/**
|
||||
* Adding resources implies setting these properties on the server side
|
||||
* - set `created_by` based on the context
|
||||
* - set `updated_by` based on the context
|
||||
* - the bookshelf `timestamps` plugin sets `created_at` and `updated_at`
|
||||
* - if plugin is disabled (e.g. import) we have a fallback condition
|
||||
*
|
||||
* Exceptions: internal context or importing
|
||||
*/
|
||||
onCreating: function onCreating(newObj, attr, options) {
|
||||
// id = 0 is still a valid value for external usage
|
||||
if (_.isUndefined(newObj.id) || _.isNull(newObj.id)) {
|
||||
newObj.setId();
|
||||
}
|
||||
|
||||
if (schema.tables[this.tableName].hasOwnProperty('created_by') && !this.get('created_by')) {
|
||||
this.set('created_by', this.contextUser(options));
|
||||
if (schema.tables[this.tableName].hasOwnProperty('created_by')) {
|
||||
if (!options.importing || (options.importing && !this.get('created_by'))) {
|
||||
this.set('created_by', this.contextUser(options));
|
||||
}
|
||||
}
|
||||
|
||||
this.set('updated_by', this.contextUser(options));
|
||||
|
||||
if (!newObj.get('created_at')) {
|
||||
newObj.set('created_at', new Date());
|
||||
}
|
||||
|
@ -144,13 +157,37 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
|||
}
|
||||
},
|
||||
|
||||
onSaving: function onSaving(newObj, attr, options) {
|
||||
onSaving: function onSaving(newObj) {
|
||||
// Remove any properties which don't belong on the model
|
||||
this.attributes = this.pick(this.permittedAttributes());
|
||||
// Store the previous attributes so we can tell what was updated later
|
||||
this._updatedAttributes = newObj.previousAttributes();
|
||||
},
|
||||
|
||||
/**
|
||||
* Changing resources implies setting these properties on the server side
|
||||
* - set `updated_by` based on the context
|
||||
* - ensure `created_at` never changes
|
||||
* - ensure `created_by` never changes
|
||||
* - the bookshelf `timestamps` plugin sets `updated_at` automatically
|
||||
*
|
||||
* Exceptions:
|
||||
* - importing data
|
||||
* - internal context
|
||||
* - if no context
|
||||
*/
|
||||
onUpdating: function onUpdating(newObj, attr, options) {
|
||||
this.set('updated_by', this.contextUser(options));
|
||||
|
||||
if (options && options.context && !options.internal && !options.importing) {
|
||||
if (newObj.hasDateChanged('created_at', {beforeWrite: true})) {
|
||||
newObj.set('created_at', this.previous('created_at'));
|
||||
}
|
||||
|
||||
if (newObj.hasChanged('created_by')) {
|
||||
newObj.set('created_by', this.previous('created_by'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
var should = require('should'),
|
||||
supertest = require('supertest'),
|
||||
testUtils = require('../../../utils'),
|
||||
moment = require('moment'),
|
||||
_ = require('lodash'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
testUtils = require('../../../utils'),
|
||||
config = require('../../../../../core/server/config'),
|
||||
ghost = testUtils.startGhost,
|
||||
markdownToMobiledoc = testUtils.DataGenerator.markdownToMobiledoc,
|
||||
|
@ -703,6 +704,47 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('check which fields can be added', function (done) {
|
||||
var newPost = {
|
||||
status: 'draft',
|
||||
title: 'title',
|
||||
mobiledoc: markdownToMobiledoc('my post'),
|
||||
created_at: moment().subtract(2, 'days').toDate(),
|
||||
updated_at: moment().subtract(2, 'days').toDate(),
|
||||
created_by: ObjectId.generate(),
|
||||
updated_by: ObjectId.generate()
|
||||
};
|
||||
|
||||
request
|
||||
.post(testUtils.API.getApiQuery('posts/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.send({posts: [newPost]})
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(201)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var response = res.body;
|
||||
res.headers.location.should.equal('/ghost/api/v0.1/posts/' + response.posts[0].id + '/?status=draft');
|
||||
should.exist(response.posts);
|
||||
response.posts.length.should.be.above(0);
|
||||
|
||||
response.posts[0].title.should.eql(newPost.title);
|
||||
response.posts[0].status.should.eql(newPost.status);
|
||||
|
||||
response.posts[0].created_at.should.not.eql(newPost.created_at.toISOString());
|
||||
response.posts[0].updated_at.should.not.eql(newPost.updated_at.toISOString());
|
||||
response.posts[0].updated_by.should.not.eql(newPost.updated_by);
|
||||
response.posts[0].created_by.should.not.eql(newPost.created_by);
|
||||
|
||||
testUtils.API.checkResponse(response.posts[0], 'post');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ## edit
|
||||
|
@ -1100,6 +1142,55 @@ describe('Post API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('check which fields can be modified', function (done) {
|
||||
var existingPostData, modifiedPostData;
|
||||
|
||||
request
|
||||
.get(testUtils.API.getApiQuery('posts/' + testUtils.DataGenerator.Content.posts[0].id + '/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.posts[0]);
|
||||
existingPostData = _.cloneDeep(jsonResponse.posts[0]);
|
||||
modifiedPostData = _.cloneDeep(jsonResponse);
|
||||
|
||||
modifiedPostData.posts[0].created_by = ObjectId.generate();
|
||||
modifiedPostData.posts[0].updated_by = ObjectId.generate();
|
||||
modifiedPostData.posts[0].created_at = moment().add(2, 'days').format();
|
||||
modifiedPostData.posts[0].updated_at = moment().add(2, 'days').format();
|
||||
|
||||
request
|
||||
.put(testUtils.API.getApiQuery('posts/' + modifiedPostData.posts[0].id + '/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.send(modifiedPostData)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.posts[0]);
|
||||
|
||||
// We expect that the changed properties aren't changed, they are still the same than before.
|
||||
jsonResponse.posts[0].created_by.should.eql(existingPostData.created_by);
|
||||
jsonResponse.posts[0].updated_by.should.eql(existingPostData.updated_by);
|
||||
jsonResponse.posts[0].created_at.should.eql(existingPostData.created_at);
|
||||
// `updated_at` is automatically set, but it's not the date we send to override.
|
||||
jsonResponse.posts[0].updated_at.should.not.eql(modifiedPostData.updated_at);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
// ## delete
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
var should = require('should'),
|
||||
_ = require('lodash'),
|
||||
supertest = require('supertest'),
|
||||
moment = require('moment'),
|
||||
testUtils = require('../../../utils'),
|
||||
ObjectId = require('bson-objectid'),
|
||||
config = require('../../../../../core/server/config'),
|
||||
|
@ -478,6 +480,56 @@ describe('User API', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('check which fields can be modified', function (done) {
|
||||
var existingUserData, modifiedUserData;
|
||||
|
||||
request.get(testUtils.API.getApiQuery('users/me/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users[0]);
|
||||
existingUserData = _.cloneDeep(jsonResponse.users[0]);
|
||||
modifiedUserData = _.cloneDeep(jsonResponse);
|
||||
|
||||
existingUserData.created_by.should.eql('1');
|
||||
existingUserData.updated_by.should.eql('1');
|
||||
|
||||
modifiedUserData.users[0].created_at = moment().add(2, 'days').format();
|
||||
modifiedUserData.users[0].updated_at = moment().add(2, 'days').format();
|
||||
modifiedUserData.users[0].created_by = ObjectId.generate();
|
||||
modifiedUserData.users[0].updated_by = ObjectId.generate();
|
||||
|
||||
delete jsonResponse.users[0].id;
|
||||
|
||||
request.put(testUtils.API.getApiQuery('users/me/'))
|
||||
.set('Authorization', 'Bearer ' + ownerAccessToken)
|
||||
.send(jsonResponse)
|
||||
.expect(200)
|
||||
.end(function (err, res) {
|
||||
/*jshint unused:false*/
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse.users[0]);
|
||||
|
||||
jsonResponse.users[0].created_by.should.eql(existingUserData.created_by);
|
||||
jsonResponse.users[0].updated_by.should.eql(existingUserData.updated_by);
|
||||
jsonResponse.users[0].updated_at.should.not.eql(modifiedUserData.updated_at);
|
||||
jsonResponse.users[0].created_at.should.eql(existingUserData.created_at);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Destroy', function () {
|
||||
|
|
Loading…
Add table
Reference in a new issue