mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-12-27 04:43:12 +03:00
c5ba27e2b5
refs: https://github.com/TryGhost/Team/issues/626 - calculated settings are simplified settings (booleans) that are based on other settings or data - they make it easier for us to determine what state features are in elsewhere in ghost e.g. admin and themes - this duplicates some of the members config concepts in the settings service
1395 lines
49 KiB
JavaScript
1395 lines
49 KiB
JavaScript
const _ = require('lodash');
|
|
const should = require('should');
|
|
const supertest = require('supertest');
|
|
const config = require('../../../../core/shared/config');
|
|
const testUtils = require('../../../utils');
|
|
const localUtils = require('./utils');
|
|
const db = require('../../../../core/server/data/db');
|
|
|
|
// NOTE: in future iterations these fields should be fetched from a central module.
|
|
// Have put a list as is here for the lack of better place for it.
|
|
const defaultSettingsKeyTypes = [
|
|
{
|
|
key: 'title',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'description',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'logo',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'cover_image',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'icon',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'lang',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'locale',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'timezone',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'codeinjection_head',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'codeinjection_foot',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'facebook',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'twitter',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'navigation',
|
|
type: 'array',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'secondary_navigation',
|
|
type: 'array',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'meta_title',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'meta_description',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'og_image',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'og_title',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'og_description',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'twitter_image',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'twitter_title',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'twitter_description',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'active_theme',
|
|
type: 'string',
|
|
group: 'theme'
|
|
},
|
|
{
|
|
key: 'is_private',
|
|
type: 'boolean',
|
|
group: 'private'
|
|
},
|
|
{
|
|
key: 'password',
|
|
type: 'string',
|
|
group: 'private'
|
|
},
|
|
{
|
|
key: 'public_hash',
|
|
type: 'string',
|
|
group: 'private'
|
|
},
|
|
{
|
|
key: 'default_content_visibility',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'default_content_visibility_tiers',
|
|
type: 'array',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_signup_access',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_from_address',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_support_address',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_reply_address',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_paid_signup_redirect',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_free_signup_redirect',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_free_price_name',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_free_price_description',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_monthly_price_id',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'members_yearly_price_id',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_product_name',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_plans',
|
|
type: 'array',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_secret_key',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_publishable_key',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_connect_secret_key',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_connect_publishable_key',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_connect_account_id',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_connect_display_name',
|
|
type: 'string',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'stripe_connect_livemode',
|
|
type: 'boolean',
|
|
group: 'members'
|
|
},
|
|
{
|
|
key: 'portal_name',
|
|
type: 'boolean',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_button',
|
|
type: 'boolean',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_plans',
|
|
type: 'array',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_products',
|
|
type: 'array',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_button_style',
|
|
type: 'string',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_button_icon',
|
|
type: 'string',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'portal_button_signup_text',
|
|
type: 'string',
|
|
group: 'portal'
|
|
},
|
|
{
|
|
key: 'mailgun_api_key',
|
|
type: 'string',
|
|
group: 'email'
|
|
},
|
|
{
|
|
key: 'mailgun_domain',
|
|
type: 'string',
|
|
group: 'email'
|
|
},
|
|
{
|
|
key: 'mailgun_base_url',
|
|
type: 'string',
|
|
group: 'email'
|
|
},
|
|
{
|
|
key: 'email_track_opens',
|
|
type: 'boolean',
|
|
group: 'email'
|
|
},
|
|
{
|
|
key: 'email_verification_required',
|
|
type: 'boolean',
|
|
group: 'email'
|
|
},
|
|
{
|
|
key: 'amp',
|
|
type: 'boolean',
|
|
group: 'amp'
|
|
},
|
|
{
|
|
key: 'amp_gtag_id',
|
|
type: 'string',
|
|
group: 'amp'
|
|
},
|
|
{
|
|
key: 'slack',
|
|
type: 'string',
|
|
group: 'slack'
|
|
},
|
|
{
|
|
key: 'slack_url',
|
|
type: 'string',
|
|
group: 'slack'
|
|
},
|
|
{
|
|
key: 'slack_username',
|
|
type: 'string',
|
|
group: 'slack'
|
|
},
|
|
{
|
|
key: 'unsplash',
|
|
type: 'boolean',
|
|
group: 'unsplash'
|
|
},
|
|
{
|
|
key: 'shared_views',
|
|
type: 'array',
|
|
group: 'views'
|
|
},
|
|
{
|
|
key: 'active_timezone',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'default_locale',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'accent_color',
|
|
type: 'string',
|
|
group: 'site'
|
|
},
|
|
{
|
|
key: 'newsletter_show_badge',
|
|
type: 'boolean',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_header_image',
|
|
type: 'string',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_show_header_icon',
|
|
type: 'boolean',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_show_header_title',
|
|
type: 'boolean',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_title_alignment',
|
|
type: 'string',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_title_font_category',
|
|
type: 'string',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_show_feature_image',
|
|
type: 'boolean',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_body_font_category',
|
|
type: 'string',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'newsletter_footer_content',
|
|
type: 'string',
|
|
group: 'newsletter'
|
|
},
|
|
{
|
|
key: 'firstpromoter',
|
|
type: 'boolean',
|
|
group: 'firstpromoter'
|
|
},
|
|
{
|
|
key: 'firstpromoter_id',
|
|
type: 'string',
|
|
group: 'firstpromoter'
|
|
},
|
|
{
|
|
key: 'oauth_client_id',
|
|
type: 'string',
|
|
group: 'oauth'
|
|
},
|
|
{
|
|
key: 'oauth_client_secret',
|
|
type: 'string',
|
|
group: 'oauth'
|
|
},
|
|
{
|
|
key: 'editor_default_email_recipients',
|
|
type: 'string',
|
|
group: 'editor'
|
|
},
|
|
{
|
|
key: 'editor_default_email_recipients_filter',
|
|
type: 'string',
|
|
group: 'editor'
|
|
},
|
|
{
|
|
key: 'editor_is_launch_complete',
|
|
type: 'boolean',
|
|
group: 'editor'
|
|
},
|
|
{
|
|
key: 'labs',
|
|
type: 'object',
|
|
group: 'labs'
|
|
}
|
|
];
|
|
|
|
const calculatedSettingsTypes = ['members_enabled', 'members_invite_only', 'paid_members_enabled'];
|
|
|
|
describe('Settings API (canary)', function () {
|
|
let request;
|
|
|
|
describe('As Owner', function () {
|
|
before(async function () {
|
|
await localUtils.startGhost();
|
|
request = supertest.agent(config.get('url'));
|
|
await localUtils.doAuth(request);
|
|
});
|
|
|
|
it('Can request all settings', function () {
|
|
return request.get(localUtils.API.getApiQuery(`settings/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse.settings);
|
|
should.exist(jsonResponse.meta);
|
|
|
|
jsonResponse.settings.should.be.an.Object();
|
|
const settings = jsonResponse.settings;
|
|
|
|
should.equal(settings.length, (defaultSettingsKeyTypes.length + calculatedSettingsTypes.length));
|
|
for (const defaultSetting of defaultSettingsKeyTypes) {
|
|
should.exist(settings.find((setting) => {
|
|
return (setting.key === defaultSetting.key)
|
|
&& (setting.type === defaultSetting.type)
|
|
&& (setting.group === defaultSetting.group);
|
|
}), `Expected to find a setting with key ${defaultSetting.key}, type ${defaultSetting.type}, and group ${defaultSetting.group}`);
|
|
}
|
|
|
|
const unsplash = settings.find(s => s.key === 'unsplash');
|
|
should.exist(unsplash);
|
|
unsplash.value.should.equal(true);
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'settings');
|
|
});
|
|
});
|
|
|
|
it('Ignores the deprecated type filter', function () {
|
|
return request.get(localUtils.API.getApiQuery(`settings/?type=theme`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse.settings);
|
|
should.exist(jsonResponse.meta);
|
|
|
|
jsonResponse.settings.should.be.an.Object();
|
|
const settings = jsonResponse.settings;
|
|
// Returns all settings
|
|
should.equal(settings.length, (defaultSettingsKeyTypes.length + calculatedSettingsTypes.length));
|
|
for (const defaultSetting of defaultSettingsKeyTypes) {
|
|
should.exist(settings.find((setting) => {
|
|
return setting.key === defaultSetting.key && setting.type === defaultSetting.type;
|
|
}), `Expected to find a setting with key ${defaultSetting.key} and type ${defaultSetting.type}`);
|
|
}
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'settings');
|
|
});
|
|
});
|
|
|
|
it('Can request settings by group', function () {
|
|
return request.get(localUtils.API.getApiQuery(`settings/?group=theme`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse.settings);
|
|
should.exist(jsonResponse.meta);
|
|
|
|
jsonResponse.settings.should.be.an.Object();
|
|
const settings = jsonResponse.settings;
|
|
|
|
Object.keys(settings).length.should.equal(1);
|
|
settings[0].key.should.equal('active_theme');
|
|
settings[0].value.should.equal('casper');
|
|
settings[0].type.should.equal('string');
|
|
settings[0].group.should.equal('theme');
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'settings');
|
|
});
|
|
});
|
|
|
|
it('Requesting core settings type ignores the parameter and returns all settings', function () {
|
|
return request.get(localUtils.API.getApiQuery(`settings/?type=core`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then((res) => {
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse.settings);
|
|
should.exist(jsonResponse.meta);
|
|
|
|
jsonResponse.settings.should.be.an.Object();
|
|
const settings = jsonResponse.settings;
|
|
|
|
Object.keys(settings).length.should.equal((defaultSettingsKeyTypes.length + calculatedSettingsTypes.length));
|
|
|
|
localUtils.API.checkResponse(jsonResponse, 'settings');
|
|
});
|
|
});
|
|
|
|
it('Can\'t read core setting', function () {
|
|
return request
|
|
.get(localUtils.API.getApiQuery('settings/db_hash/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(403);
|
|
});
|
|
|
|
it('Can\'t read secret setting', function (done) {
|
|
const key = 'stripe_secret_key';
|
|
request
|
|
.get(localUtils.API.getApiQuery(`settings/${key}/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
const json = res.body;
|
|
should.exist(json);
|
|
should.exist(json.settings);
|
|
|
|
json.settings.length.should.eql(1);
|
|
json.settings[0].key.should.eql('stripe_secret_key');
|
|
should(json.settings[0].value).be.null();
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can\'t read secret setting', function (done) {
|
|
const key = 'stripe_secret_key';
|
|
request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send({
|
|
settings: [{
|
|
key,
|
|
value: 'sk_test_4eC39HqLyjWDarjtT1zdp7dc'
|
|
}]
|
|
})
|
|
.then(() => {
|
|
request
|
|
.get(localUtils.API.getApiQuery(`settings/${key}/`))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
const json = res.body;
|
|
should.exist(json);
|
|
should.exist(json.settings);
|
|
|
|
json.settings.length.should.eql(1);
|
|
json.settings[0].key.should.eql('stripe_secret_key');
|
|
json.settings[0].value.should.eql('••••••••');
|
|
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can\'t read permalinks', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/permalinks/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(404)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can read slack_url introduced in v4', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/slack_url/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
jsonResponse.settings[0].key.should.eql('slack_url');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can read labs', async function () {
|
|
const res = await request.get(localUtils.API.getApiQuery('settings/labs/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
|
|
const jsonObjectRegex = /^\{.*\}$/; // '{...}'
|
|
jsonResponse.settings[0].key.should.eql('labs');
|
|
jsonResponse.settings[0].value.should.match(jsonObjectRegex);
|
|
});
|
|
|
|
it('Can read deprecated default_locale', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/default_locale/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('default_locale');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can edit deprecated default_locale setting', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/default_locale/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
const newValue = 'new value';
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'default_locale', value: 'ua'}];
|
|
|
|
return jsonResponse;
|
|
})
|
|
.then((editedSetting) => {
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(editedSetting)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then(function (res) {
|
|
should.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('default_locale');
|
|
jsonResponse.settings[0].value.should.eql('ua');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can edit deprecated lang setting', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/lang/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'lang', value: 'ua'}];
|
|
|
|
return jsonResponse;
|
|
})
|
|
.then((editedSetting) => {
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(editedSetting)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then(function (res) {
|
|
should.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('lang');
|
|
jsonResponse.settings[0].value.should.eql('ua');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can edit newly introduced locale setting', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/locale/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'locale', value: 'ge'}];
|
|
|
|
return jsonResponse;
|
|
})
|
|
.then((editedSetting) => {
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(editedSetting)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then(function (res) {
|
|
should.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('locale');
|
|
jsonResponse.settings[0].value.should.eql('ge');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can read timezone', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/timezone/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('timezone');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can read active_timezone', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/active_timezone/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('active_timezone');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can read deprecated active_timezone', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/active_timezone/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('active_timezone');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can read slack renamed&reformatted in v4', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/slack/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('slack');
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Format of unsplash is boolean as introduced with v4', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/unsplash/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('unsplash');
|
|
JSON.parse(jsonResponse.settings[0].value).should.eql(true);
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can\'t read non existent setting', function (done) {
|
|
request.get(localUtils.API.getApiQuery('settings/testsetting/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(404)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
should.not.exist(res.headers['x-cache-invalidate']);
|
|
const jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.errors);
|
|
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
|
'message',
|
|
'context',
|
|
'type',
|
|
'details',
|
|
'property',
|
|
'help',
|
|
'code',
|
|
'id',
|
|
'ghostErrorCode'
|
|
]);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can\'t edit permalinks', function (done) {
|
|
const settingToChange = {
|
|
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
|
|
};
|
|
|
|
request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(404)
|
|
.end(function (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('Can edit only allowed labs keys', async function () {
|
|
const settingToChange = {
|
|
settings: [{
|
|
key: 'labs',
|
|
value: JSON.stringify({
|
|
activitypub: true,
|
|
gibberish: true
|
|
})
|
|
}]
|
|
};
|
|
|
|
const res = await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
|
|
const jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
jsonResponse.settings.length.should.eql(1);
|
|
testUtils.API.checkResponseValue(jsonResponse.settings[0], ['id', 'group', 'key', 'value', 'type', 'flags', 'created_at', 'updated_at']);
|
|
jsonResponse.settings[0].key.should.eql('labs');
|
|
|
|
const responseObj = JSON.parse(jsonResponse.settings[0].value);
|
|
|
|
should.not.exist(responseObj.gibberish);
|
|
});
|
|
|
|
it('Can\'t edit non existent setting', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
const newValue = 'new value';
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
|
|
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(jsonResponse)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(404)
|
|
.then(function ({body, headers}) {
|
|
jsonResponse = body;
|
|
should.not.exist(headers['x-cache-invalidate']);
|
|
should.exist(jsonResponse.errors);
|
|
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
|
'message',
|
|
'context',
|
|
'type',
|
|
'details',
|
|
'property',
|
|
'help',
|
|
'code',
|
|
'id',
|
|
'ghostErrorCode'
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Will transform "1"', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
const jsonResponse = res.body;
|
|
|
|
const settingToChange = {
|
|
settings: [
|
|
{
|
|
key: 'is_private',
|
|
value: '1'
|
|
}
|
|
]
|
|
};
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200)
|
|
.then(function ({body, headers}) {
|
|
const putBody = body;
|
|
headers['x-cache-invalidate'].should.eql('/*');
|
|
should.exist(putBody);
|
|
|
|
putBody.settings[0].key.should.eql('is_private');
|
|
putBody.settings[0].value.should.eql(true);
|
|
|
|
localUtils.API.checkResponse(putBody, 'settings');
|
|
});
|
|
});
|
|
});
|
|
|
|
it('Can edit multiple setting along with a deprecated one from v4', async function () {
|
|
const settingToChange = {
|
|
settings: [
|
|
{
|
|
key: 'slack',
|
|
value: JSON.stringify([{
|
|
url: 'https://newurl.tld/slack',
|
|
username: 'New Slack Username'
|
|
}])
|
|
}, {
|
|
key: 'unsplash',
|
|
value: true
|
|
}, {
|
|
key: 'title',
|
|
value: 'New Value'
|
|
}
|
|
]
|
|
};
|
|
|
|
const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
|
|
const putBody = body;
|
|
headers['x-cache-invalidate'].should.eql('/*');
|
|
should.exist(putBody);
|
|
|
|
putBody.settings.length.should.equal(3);
|
|
|
|
putBody.settings[0].key.should.eql('unsplash');
|
|
should.equal(putBody.settings[0].value, true);
|
|
|
|
putBody.settings[1].key.should.eql('title');
|
|
should.equal(putBody.settings[1].value, 'New Value');
|
|
|
|
putBody.settings[2].key.should.eql('slack');
|
|
should.equal(putBody.settings[2].value, JSON.stringify([{
|
|
url: 'https://newurl.tld/slack',
|
|
username: 'New Slack Username'
|
|
}]));
|
|
|
|
localUtils.API.checkResponse(putBody, 'settings');
|
|
});
|
|
|
|
it('Can edit a setting introduced in v4', async function () {
|
|
const settingToChange = {
|
|
settings: [
|
|
{
|
|
key: 'slack_username',
|
|
value: 'can edit me'
|
|
}
|
|
]
|
|
};
|
|
|
|
const {body, headers} = await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
|
|
const putBody = body;
|
|
headers['x-cache-invalidate'].should.eql('/*');
|
|
should.exist(putBody);
|
|
|
|
putBody.settings.length.should.equal(1);
|
|
|
|
localUtils.API.checkResponse(putBody, 'settings');
|
|
putBody.settings[0].key.should.eql('slack_username');
|
|
putBody.settings[0].value.should.eql('can edit me');
|
|
});
|
|
|
|
it('Can edit URLs without internal storage format leaking', async function () {
|
|
const settingsToChange = {
|
|
settings: [
|
|
{key: 'cover_image', value: `${config.get('url')}/content/images/cover_image.png`},
|
|
{key: 'logo', value: `${config.get('url')}/content/images/logo.png`},
|
|
{key: 'icon', value: `${config.get('url')}/content/images/icon.png`},
|
|
{key: 'portal_button_icon', value: `${config.get('url')}/content/images/portal_button_icon.png`},
|
|
{key: 'og_image', value: `${config.get('url')}/content/images/og_image.png`},
|
|
{key: 'twitter_image', value: `${config.get('url')}/content/images/twitter_image.png`}
|
|
]
|
|
};
|
|
|
|
const {body} = await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingsToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(200);
|
|
|
|
const responseSettings = body.settings.reduce((acc, setting) => {
|
|
acc[setting.key] = setting.value;
|
|
return acc;
|
|
}, {});
|
|
|
|
responseSettings.should.have.property('cover_image', `${config.get('url')}/content/images/cover_image.png`);
|
|
responseSettings.should.have.property('logo', `${config.get('url')}/content/images/logo.png`);
|
|
responseSettings.should.have.property('icon', `${config.get('url')}/content/images/icon.png`);
|
|
responseSettings.should.have.property('portal_button_icon', `${config.get('url')}/content/images/portal_button_icon.png`);
|
|
responseSettings.should.have.property('og_image', `${config.get('url')}/content/images/og_image.png`);
|
|
responseSettings.should.have.property('twitter_image', `${config.get('url')}/content/images/twitter_image.png`);
|
|
|
|
const dbSettingsRows = await db.knex('settings')
|
|
.select('key', 'value')
|
|
.whereIn('key', ['cover_image', 'logo', 'icon', 'portal_button_icon', 'og_image', 'twitter_image']);
|
|
|
|
const dbSettings = dbSettingsRows.reduce((acc, setting) => {
|
|
acc[setting.key] = setting.value;
|
|
return acc;
|
|
}, {});
|
|
|
|
dbSettings.should.have.property('cover_image', '__GHOST_URL__/content/images/cover_image.png');
|
|
dbSettings.should.have.property('logo', '__GHOST_URL__/content/images/logo.png');
|
|
dbSettings.should.have.property('icon', '__GHOST_URL__/content/images/icon.png');
|
|
dbSettings.should.have.property('portal_button_icon', '__GHOST_URL__/content/images/portal_button_icon.png');
|
|
dbSettings.should.have.property('og_image', '__GHOST_URL__/content/images/og_image.png');
|
|
dbSettings.should.have.property('twitter_image', '__GHOST_URL__/content/images/twitter_image.png');
|
|
});
|
|
|
|
it('Can only send array values for keys defined with array type', async function () {
|
|
const settingsToChange = {
|
|
settings: [
|
|
{key: 'navigation', value: 'not an array'}
|
|
]
|
|
};
|
|
|
|
await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingsToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(422);
|
|
});
|
|
|
|
it('Cannot edit notifications key through API', async function () {
|
|
const settingsToChange = {
|
|
settings: [
|
|
{key: 'notifications', value: JSON.stringify(['do not touch me'])}
|
|
]
|
|
};
|
|
|
|
await request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(settingsToChange)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(403);
|
|
});
|
|
});
|
|
|
|
describe('As Admin', function () {
|
|
before(async function () {
|
|
await localUtils.startGhost();
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
// create admin
|
|
const admin = await testUtils.createUser({
|
|
user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}),
|
|
role: testUtils.DataGenerator.Content.roles[0].name
|
|
});
|
|
request.user = admin;
|
|
// by default we login with the owner
|
|
await localUtils.doAuth(request);
|
|
});
|
|
});
|
|
|
|
describe('As Editor', function () {
|
|
before(async function () {
|
|
await localUtils.startGhost();
|
|
request = supertest.agent(config.get('url'));
|
|
// create editor
|
|
request.user = await testUtils.createUser({
|
|
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
|
|
role: testUtils.DataGenerator.Content.roles[1].name
|
|
});
|
|
|
|
// by default we login with the owner
|
|
await localUtils.doAuth(request);
|
|
});
|
|
|
|
it('should not be able to edit settings', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
|
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(jsonResponse)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(403)
|
|
.then(function ({body, headers}) {
|
|
jsonResponse = body;
|
|
should.not.exist(headers['x-cache-invalidate']);
|
|
should.exist(jsonResponse.errors);
|
|
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
|
'message',
|
|
'context',
|
|
'type',
|
|
'details',
|
|
'property',
|
|
'help',
|
|
'code',
|
|
'id',
|
|
'ghostErrorCode'
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
|
|
describe('As Author', function () {
|
|
before(async function () {
|
|
await localUtils.startGhost();
|
|
request = supertest.agent(config.get('url'));
|
|
|
|
// create author
|
|
request.user = await testUtils.createUser({
|
|
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
|
|
role: testUtils.DataGenerator.Content.roles[2].name
|
|
});
|
|
|
|
// by default we login with the owner
|
|
await localUtils.doAuth(request);
|
|
});
|
|
|
|
it('should not be able to edit settings', function () {
|
|
return request.get(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.set('Accept', 'application/json')
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.then(function (res) {
|
|
let jsonResponse = res.body;
|
|
should.exist(jsonResponse);
|
|
should.exist(jsonResponse.settings);
|
|
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
|
|
|
return request.put(localUtils.API.getApiQuery('settings/'))
|
|
.set('Origin', config.get('url'))
|
|
.send(jsonResponse)
|
|
.expect('Content-Type', /json/)
|
|
.expect('Cache-Control', testUtils.cacheRules.private)
|
|
.expect(403)
|
|
.then(function ({body, headers}) {
|
|
jsonResponse = body;
|
|
should.not.exist(headers['x-cache-invalidate']);
|
|
should.exist(jsonResponse.errors);
|
|
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
|
'message',
|
|
'context',
|
|
'type',
|
|
'details',
|
|
'property',
|
|
'help',
|
|
'code',
|
|
'id',
|
|
'ghostErrorCode'
|
|
]);
|
|
});
|
|
});
|
|
});
|
|
});
|
|
});
|