0
Fork 0
mirror of https://github.com/TryGhost/Ghost.git synced 2025-01-20 22:42:53 -05:00

🐛 Dynamic Routing Beta: Disallow using author resource and author data key name (#9790)

* 🐛 Dynamic Routing Beta: Shortform `author.foo` is not allowed

refs #9601

- otherwise you get access to {{author}} in the theme, which is deprecated & causes errors
- recommend not using {{author}} as data longform name in the yaml validator
- (internal usage is still allowed)
- also warn against using reserved data key names like filter, resource, limit etc - maybe remove this restriction later but seems like a sensible validation right now.
This commit is contained in:
Katharina Irrgang 2018-08-16 11:48:33 +02:00
parent fd8502448a
commit cd690efad1
4 changed files with 136 additions and 3 deletions

View file

@ -54,7 +54,7 @@ class TaxonomyRouter extends ParentRouter {
type: 'channel',
name: this.taxonomyKey,
permalinks: this.permalinks.getValue(),
data: {[this.taxonomyKey]: _.omit(RESOURCE_CONFIG.QUERY[this.taxonomyKey], 'alias')},
data: {[this.taxonomyKey]: _.omit(RESOURCE_CONFIG.QUERY[this.taxonomyKey], ['alias', 'internal'])},
filter: RESOURCE_CONFIG.TAXONOMIES[this.taxonomyKey].filter,
resourceType: this.getResourceType(),
context: [this.taxonomyKey],

View file

@ -10,6 +10,16 @@ module.exports.QUERY = {
}
},
author: {
internal: true,
alias: 'users',
type: 'read',
resource: 'users',
options: {
slug: '%s',
visibility: 'public'
}
},
user: {
alias: 'users',
type: 'read',
resource: 'users',

View file

@ -48,6 +48,15 @@ _private.validateData = function validateData(object) {
let [resourceKey, slug] = shortForm.split('.');
// @NOTE: `data: author.foo` is not allowed currently, because this will make {{author}} available in the theme, which is deprecated (single author usage)
if (!RESOURCE_CONFIG.QUERY[resourceKey] ||
(RESOURCE_CONFIG.QUERY[resourceKey].hasOwnProperty('internal') && RESOURCE_CONFIG.QUERY[resourceKey].internal === true)) {
throw new common.errors.ValidationError({
message: `Resource key not supported. ${resourceKey}`,
help: 'Please use: tag, user, post or page.'
});
}
longForm.query[options.resourceKey || resourceKey] = {};
longForm.query[options.resourceKey || resourceKey] = _.omit(_.cloneDeep(RESOURCE_CONFIG.QUERY[resourceKey]), 'alias');
@ -81,7 +90,22 @@ _private.validateData = function validateData(object) {
};
_.each(object.data, (value, key) => {
// CASE: short form e.g. data: tag.recipes
// CASE: a name is required to define the data longform
if (['resource', 'type', 'limit', 'order', 'include', 'filter', 'status', 'visibility', 'slug', 'redirect'].indexOf(key) !== -1) {
throw new common.errors.ValidationError({
message: 'Please wrap the data definition into a custom name.',
help: 'Example:\n data:\n my-tag:\n resource: tags\n ...\n'
});
}
// @NOTE: We disallow author, because {{author}} is deprecated.
if (key === 'author') {
throw new common.errors.ValidationError({
message: 'Please choose a different name. We recommend not using author.'
});
}
// CASE: short form used with custom names, resolve to longform and return
if (typeof object.data[key] === 'string') {
const longForm = shortToLongForm(object.data[key], {resourceKey: key});
data.query = _.merge(data.query, longForm.query);

View file

@ -326,6 +326,9 @@ describe('UNIT: services/settings/validate', function () {
'/music/': {
data: 'tag.music'
},
'/ghost/': {
data: 'user.ghost'
},
'/sleep/': {
data: {
bed: 'tag.bed',
@ -374,6 +377,24 @@ describe('UNIT: services/settings/validate', function () {
},
templates: []
},
'/ghost/': {
data: {
query: {
user: {
resource: 'users',
type: 'read',
options: {
slug: 'ghost',
visibility: 'public'
}
}
},
router: {
users: [{redirect: true, slug: 'ghost'}]
}
},
templates: []
},
'/music/': {
data: {
query: {
@ -609,7 +630,7 @@ describe('UNIT: services/settings/validate', function () {
});
});
it('errors', function () {
it('errors: data shortform incorrect', function () {
try {
validate({
collections: {
@ -619,7 +640,16 @@ describe('UNIT: services/settings/validate', function () {
}
}
});
} catch (err) {
(err instanceof common.errors.ValidationError).should.be.true();
return;
}
throw new Error('should fail');
});
it('errors: data longform resource is missing', function () {
try {
validate({
collections: {
'/magic/': {
@ -630,7 +660,16 @@ describe('UNIT: services/settings/validate', function () {
}
}
});
} catch (err) {
(err instanceof common.errors.ValidationError).should.be.true();
return;
}
throw new Error('should fail');
});
it('errors: data longform type is missing', function () {
try {
validate({
collections: {
'/magic/': {
@ -648,5 +687,65 @@ describe('UNIT: services/settings/validate', function () {
throw new Error('should fail');
});
it('errors: data shortform author is not allowed', function () {
try {
validate({
collections: {
'/magic/': {
permalink: '/{slug}/',
data: 'author.food'
}
}
});
} catch (err) {
(err instanceof common.errors.ValidationError).should.be.true();
return;
}
throw new Error('should fail');
});
it('errors: data longform name is author', function () {
try {
validate({
collections: {
'/magic/': {
permalink: '/{slug}/',
data: {
author: {
resource: 'users'
}
}
}
}
});
} catch (err) {
(err instanceof common.errors.ValidationError).should.be.true();
return;
}
throw new Error('should fail');
});
it('errors: data longform does not use a custom name at all', function () {
try {
validate({
collections: {
'/magic/': {
permalink: '/{slug}/',
data: {
resource: 'users'
}
}
}
});
} catch (err) {
(err instanceof common.errors.ValidationError).should.be.true();
return;
}
throw new Error('should fail');
});
});
});