mirror of
https://github.com/TryGhost/Ghost.git
synced 2024-11-30 11:54:33 +03:00
Merge branch 'master' into v3
This commit is contained in:
commit
587bd8accb
@ -81,6 +81,7 @@ function updateLocalTemplateOptions(req, res, next) {
|
||||
const member = req.member ? {
|
||||
email: req.member.email,
|
||||
name: req.member.name,
|
||||
firstname: req.member.name && req.member.name.split(' ')[0],
|
||||
subscriptions: req.member.stripe.subscriptions,
|
||||
paid: req.member.stripe.subscriptions.length !== 0
|
||||
} : null;
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const common = require('../../lib/common');
|
||||
const urlUtils = require('../../lib/url-utils');
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles'];
|
||||
const UNSAFE_ATTRS = ['status', 'authors'];
|
||||
const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'pages',
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const common = require('../../lib/common');
|
||||
const urlUtils = require('../../lib/url-utils');
|
||||
const allowedIncludes = ['tags', 'authors', 'authors.roles'];
|
||||
const unsafeAttrs = ['status', 'authors'];
|
||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'posts',
|
||||
|
@ -83,6 +83,9 @@ module.exports = {
|
||||
cacheInvalidate: true
|
||||
},
|
||||
permissions: {
|
||||
unsafeAttrsObject(frame) {
|
||||
return _.find(frame.data.settings, {key: 'labs'});
|
||||
},
|
||||
before(frame) {
|
||||
const errors = [];
|
||||
|
||||
|
@ -26,7 +26,12 @@ const nonePublicAuth = (apiConfig, frame) => {
|
||||
permissionIdentifier = apiConfig.identifier(frame);
|
||||
}
|
||||
|
||||
const unsafeAttrObject = apiConfig.unsafeAttrs && _.has(frame, `data.[${apiConfig.docName}][0]`) ? _.pick(frame.data[apiConfig.docName][0], apiConfig.unsafeAttrs) : {};
|
||||
let unsafeAttrObject = apiConfig.unsafeAttrs && _.has(frame, `data.[${apiConfig.docName}][0]`) ? _.pick(frame.data[apiConfig.docName][0], apiConfig.unsafeAttrs) : {};
|
||||
|
||||
if (apiConfig.unsafeAttrsObject) {
|
||||
unsafeAttrObject = apiConfig.unsafeAttrsObject(frame);
|
||||
}
|
||||
|
||||
const permsPromise = permissions.canThis(frame.options.context)[apiConfig.method][singular](permissionIdentifier, unsafeAttrObject);
|
||||
|
||||
return permsPromise.then((result) => {
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const common = require('../../lib/common');
|
||||
const urlUtils = require('../../lib/url-utils');
|
||||
const ALLOWED_INCLUDES = ['tags', 'authors', 'authors.roles'];
|
||||
const UNSAFE_ATTRS = ['status', 'authors'];
|
||||
const UNSAFE_ATTRS = ['status', 'authors', 'visibility'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'pages',
|
||||
|
@ -2,7 +2,7 @@ const models = require('../../models');
|
||||
const common = require('../../lib/common');
|
||||
const urlUtils = require('../../lib/url-utils');
|
||||
const allowedIncludes = ['tags', 'authors', 'authors.roles'];
|
||||
const unsafeAttrs = ['status', 'authors'];
|
||||
const unsafeAttrs = ['status', 'authors', 'visibility'];
|
||||
|
||||
module.exports = {
|
||||
docName: 'posts',
|
||||
|
@ -83,6 +83,9 @@ module.exports = {
|
||||
cacheInvalidate: true
|
||||
},
|
||||
permissions: {
|
||||
unsafeAttrsObject(frame) {
|
||||
return _.find(frame.data.settings, {key: 'labs'});
|
||||
},
|
||||
before(frame) {
|
||||
const errors = [];
|
||||
|
||||
|
@ -0,0 +1,34 @@
|
||||
const common = require('../../../../lib/common');
|
||||
const commands = require('../../../schema/commands');
|
||||
|
||||
module.exports = {
|
||||
config: {
|
||||
transaction: true
|
||||
},
|
||||
|
||||
async up(options){
|
||||
const conn = options.transacting || options.connection;
|
||||
const hasTable = await conn.schema.hasTable('members_stripe_customers_subscriptions');
|
||||
|
||||
if (hasTable) {
|
||||
common.logging.warn('Adding table: members_stripe_customers_subscriptions');
|
||||
return;
|
||||
}
|
||||
|
||||
common.logging.info('Adding table: members_stripe_customers_subscriptions');
|
||||
return commands.createTable('members_stripe_customers_subscriptions', conn);
|
||||
},
|
||||
|
||||
async down(options){
|
||||
const conn = options.transacting || options.connection;
|
||||
const hasTable = await conn.schema.hasTable('members_stripe_customers_subscriptions');
|
||||
|
||||
if (!hasTable) {
|
||||
common.logging.warn('Dropping table: members_stripe_customers_subscriptions');
|
||||
return;
|
||||
}
|
||||
|
||||
common.logging.info('Dropping table: members_stripe_customers_subscriptions');
|
||||
return commands.deleteTable('members_stripe_customers_subscriptions', conn);
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
const commands = require('../../../schema').commands;
|
||||
|
||||
module.exports = {
|
||||
|
||||
up: commands.createColumnMigration({
|
||||
table: 'members_stripe_customers',
|
||||
column: 'email',
|
||||
dbIsInCorrectState(hasColumn) {
|
||||
return hasColumn === true;
|
||||
},
|
||||
operation: commands.addColumn,
|
||||
operationVerb: 'Adding'
|
||||
}),
|
||||
|
||||
down: commands.createColumnMigration({
|
||||
table: 'members_stripe_customers',
|
||||
column: 'email',
|
||||
dbIsInCorrectState(hasColumn) {
|
||||
return hasColumn === false;
|
||||
},
|
||||
operation: commands.dropColumn,
|
||||
operationVerb: 'Dropping'
|
||||
}),
|
||||
|
||||
config: {
|
||||
transaction: true
|
||||
}
|
||||
};
|
@ -0,0 +1,28 @@
|
||||
const commands = require('../../../schema').commands;
|
||||
|
||||
module.exports = {
|
||||
|
||||
up: commands.createColumnMigration({
|
||||
table: 'members_stripe_customers',
|
||||
column: 'name',
|
||||
dbIsInCorrectState(hasColumn) {
|
||||
return hasColumn === true;
|
||||
},
|
||||
operation: commands.addColumn,
|
||||
operationVerb: 'Adding'
|
||||
}),
|
||||
|
||||
down: commands.createColumnMigration({
|
||||
table: 'members_stripe_customers',
|
||||
column: 'name',
|
||||
dbIsInCorrectState(hasColumn) {
|
||||
return hasColumn === false;
|
||||
},
|
||||
operation: commands.dropColumn,
|
||||
operationVerb: 'Dropping'
|
||||
}),
|
||||
|
||||
config: {
|
||||
transaction: true
|
||||
}
|
||||
};
|
@ -198,7 +198,7 @@
|
||||
"defaultValue": "public"
|
||||
},
|
||||
"members_subscription_settings": {
|
||||
"defaultValue": "{\"isPaid\":false,\"paymentProcessors\":[{\"adapter\":\"stripe\",\"config\":{\"secret_token\":\"\",\"public_token\":\"\",\"product\":{\"name\":\"Ghost Subscription\"},\"plans\":[{\"name\":\"Monthly\",\"currency\":\"usd\",\"interval\":\"month\",\"amount\":\"\"},{\"name\":\"Yearly\",\"currency\":\"usd\",\"interval\":\"year\",\"amount\":\"\"}]}}]}"
|
||||
"defaultValue": "{\"isPaid\":false,\"fromAddress\":\"noreply\",\"requirePaymentForSignup\":false,\"paymentProcessors\":[{\"adapter\":\"stripe\",\"config\":{\"secret_token\":\"\",\"public_token\":\"\",\"product\":{\"name\":\"Ghost Subscription\"},\"plans\":[{\"name\":\"Monthly\",\"currency\":\"usd\",\"interval\":\"month\",\"amount\":\"\"},{\"name\":\"Yearly\",\"currency\":\"usd\",\"interval\":\"year\",\"amount\":\"\"}]}}]}"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -333,11 +333,32 @@ module.exports = {
|
||||
member_id: {type: 'string', maxlength: 24, nullable: false, unique: false},
|
||||
// customer_id is unique: false because mysql with innodb utf8mb4 cannot have unqiue columns larger than 191 chars
|
||||
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
|
||||
name: {type: 'string', maxlength: 191, nullable: true},
|
||||
email: {type: 'string', maxlength: 191, nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'string', maxlength: 24, nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'string', maxlength: 24, nullable: true}
|
||||
},
|
||||
members_stripe_customers_subscriptions: {
|
||||
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
||||
customer_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
|
||||
subscription_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
|
||||
plan_id: {type: 'string', maxlength: 255, nullable: false, unique: false},
|
||||
status: {type: 'string', maxlength: 50, nullable: false},
|
||||
current_period_end: {type: 'dateTime', nullable: false},
|
||||
start_date: {type: 'dateTime', nullable: false},
|
||||
default_payment_card_last4: {type: 'string', maxlength: 4, nullable: true},
|
||||
created_at: {type: 'dateTime', nullable: false},
|
||||
created_by: {type: 'string', maxlength: 24, nullable: false},
|
||||
updated_at: {type: 'dateTime', nullable: true},
|
||||
updated_by: {type: 'string', maxlength: 24, nullable: true},
|
||||
/* Below fields eventually should be normalised e.g. stripe_plans table, link to here on plan_id */
|
||||
plan_nickname: {type: 'string', maxlength: 50, nullable: false},
|
||||
plan_interval: {type: 'string', maxlength: 50, nullable: false},
|
||||
plan_amount: {type: 'integer', nullable: false},
|
||||
plan_currency: {type: 'string', maxLength: 3, nullable: false}
|
||||
},
|
||||
actions: {
|
||||
id: {type: 'string', maxlength: 24, nullable: false, primary: true},
|
||||
resource_id: {type: 'string', maxlength: 24, nullable: true},
|
||||
|
@ -684,7 +684,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({
|
||||
case 'findOne':
|
||||
return baseOptions.concat(extraOptions, ['columns', 'require', 'mongoTransformer']);
|
||||
case 'findAll':
|
||||
return baseOptions.concat(extraOptions, ['columns', 'mongoTransformer']);
|
||||
return baseOptions.concat(extraOptions, ['filter', 'columns', 'mongoTransformer']);
|
||||
case 'findPage':
|
||||
return baseOptions.concat(extraOptions, ['filter', 'order', 'page', 'limit', 'columns', 'mongoTransformer']);
|
||||
default:
|
||||
|
@ -35,7 +35,8 @@ models = [
|
||||
'member',
|
||||
'action',
|
||||
'posts-meta',
|
||||
'member-stripe-customer'
|
||||
'member-stripe-customer',
|
||||
'stripe-customer-subscription'
|
||||
];
|
||||
|
||||
function init() {
|
||||
|
@ -2,6 +2,17 @@ const ghostBookshelf = require('./base');
|
||||
|
||||
const MemberStripeCustomer = ghostBookshelf.Model.extend({
|
||||
tableName: 'members_stripe_customers'
|
||||
}, {
|
||||
async upsert(data, unfilteredOptions) {
|
||||
const customerId = data.customer_id;
|
||||
const model = await this.findOne({customer_id: customerId}, unfilteredOptions);
|
||||
if (model) {
|
||||
return this.edit(data, Object.assign({}, unfilteredOptions, {
|
||||
id: model.id
|
||||
}));
|
||||
}
|
||||
return this.add(data, unfilteredOptions);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
|
@ -905,7 +905,14 @@ Post = ghostBookshelf.Model.extend({
|
||||
|
||||
// NOTE: the `authors` extension is the parent of the post model. It also has a permissible function.
|
||||
permissible: function permissible(postModel, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasAppPermission, hasApiKeyPermission) {
|
||||
let isContributor, isEdit, isAdd, isDestroy;
|
||||
let isContributor;
|
||||
let isOwner;
|
||||
let isAdmin;
|
||||
let isEditor;
|
||||
let isIntegration;
|
||||
let isEdit;
|
||||
let isAdd;
|
||||
let isDestroy;
|
||||
|
||||
function isChanging(attr) {
|
||||
return unsafeAttrs[attr] && unsafeAttrs[attr] !== postModel.get(attr);
|
||||
@ -920,6 +927,11 @@ Post = ghostBookshelf.Model.extend({
|
||||
}
|
||||
|
||||
isContributor = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Contributor'});
|
||||
isOwner = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'});
|
||||
isAdmin = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Admin'});
|
||||
isEditor = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Editor'});
|
||||
isIntegration = loadedPermissions.apiKey && _.some(loadedPermissions.apiKey.roles, {name: 'Admin Integration'});
|
||||
|
||||
isEdit = (action === 'edit');
|
||||
isAdd = (action === 'add');
|
||||
isDestroy = (action === 'destroy');
|
||||
@ -933,6 +945,8 @@ Post = ghostBookshelf.Model.extend({
|
||||
} else if (isContributor && isDestroy) {
|
||||
// If destroying, only allow contributor to destroy their own draft posts
|
||||
hasUserPermission = isDraft();
|
||||
} else if (!(isOwner || isAdmin || isEditor || isIntegration)) {
|
||||
hasUserPermission = !isChanging('visibility');
|
||||
}
|
||||
|
||||
const excludedAttrs = [];
|
||||
|
@ -6,6 +6,7 @@ const Promise = require('bluebird'),
|
||||
ghostBookshelf = require('./base'),
|
||||
common = require('../lib/common'),
|
||||
validation = require('../data/validation'),
|
||||
settingsCache = require('../services/settings/cache'),
|
||||
internalContext = {context: {internal: true}};
|
||||
|
||||
let Settings, defaultSettings;
|
||||
@ -235,6 +236,35 @@ Settings = ghostBookshelf.Model.extend({
|
||||
|
||||
return allSettings;
|
||||
});
|
||||
},
|
||||
|
||||
permissible: function permissible(modelId, action, context, unsafeAttrs, loadedPermissions, hasUserPermission, hasAppPermission, hasApiKeyPermission) {
|
||||
let isEdit = (action === 'edit');
|
||||
let isOwner;
|
||||
|
||||
function isChangingMembers() {
|
||||
if (unsafeAttrs && unsafeAttrs.key === 'labs') {
|
||||
let editedValue = JSON.parse(unsafeAttrs.value);
|
||||
if (editedValue.members !== undefined) {
|
||||
return editedValue.members !== settingsCache.get('labs').members;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isOwner = loadedPermissions.user && _.some(loadedPermissions.user.roles, {name: 'Owner'});
|
||||
|
||||
if (isEdit && isChangingMembers()) {
|
||||
// Only allow owner to toggle members flag
|
||||
hasUserPermission = isOwner;
|
||||
}
|
||||
|
||||
if (hasUserPermission && hasApiKeyPermission && hasAppPermission) {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
return Promise.reject(new common.errors.NoPermissionError({
|
||||
message: common.i18n.t('errors.models.post.notEnoughPermission')
|
||||
}));
|
||||
}
|
||||
});
|
||||
|
||||
|
20
core/server/models/stripe-customer-subscription.js
Normal file
20
core/server/models/stripe-customer-subscription.js
Normal file
@ -0,0 +1,20 @@
|
||||
const ghostBookshelf = require('./base');
|
||||
|
||||
const StripeCustomerSubscription = ghostBookshelf.Model.extend({
|
||||
tableName: 'members_stripe_customers_subscriptions'
|
||||
}, {
|
||||
async upsert(data, unfilteredOptions) {
|
||||
const subscriptionId = unfilteredOptions.subscription_id;
|
||||
const model = await this.findOne({subscription_id: subscriptionId}, unfilteredOptions);
|
||||
if (model) {
|
||||
return this.edit(data, Object.assign({}, unfilteredOptions, {
|
||||
id: model.id
|
||||
}));
|
||||
}
|
||||
return this.add(data, unfilteredOptions);
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = {
|
||||
StripeCustomerSubscription: ghostBookshelf.model('StripeCustomerSubscription', StripeCustomerSubscription)
|
||||
};
|
@ -138,3 +138,9 @@ Array.prototype.forEach.call(document.querySelectorAll('[data-members-signout]')
|
||||
}
|
||||
el.addEventListener('click', clickHandler);
|
||||
});
|
||||
|
||||
var url = new URL(window.location);
|
||||
if (url.searchParams.get('token')) {
|
||||
url.searchParams.delete('token');
|
||||
window.history.replaceState({}, document.title, url.href);
|
||||
}
|
||||
|
@ -28,23 +28,46 @@ function getMember(data, options = {}) {
|
||||
});
|
||||
}
|
||||
|
||||
async function setMemberMetadata(member, module, metadata) {
|
||||
async function setMetadata(module, metadata) {
|
||||
if (module !== 'stripe') {
|
||||
return;
|
||||
}
|
||||
await models.Member.edit({
|
||||
stripe_customers: metadata
|
||||
}, {id: member.id, withRelated: ['stripe_customers']});
|
||||
|
||||
if (metadata.customer) {
|
||||
await models.MemberStripeCustomer.upsert(metadata.customer, {
|
||||
customer_id: metadata.customer.customer_id
|
||||
});
|
||||
}
|
||||
|
||||
if (metadata.subscription) {
|
||||
await models.StripeCustomerSubscription.upsert(metadata.subscription, {
|
||||
subscription_id: metadata.subscription.subscription_id
|
||||
});
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
async function getMemberMetadata(member, module) {
|
||||
async function getMetadata(module, member) {
|
||||
if (module !== 'stripe') {
|
||||
return;
|
||||
}
|
||||
const model = await models.Member.where({id: member.id}).fetch({withRelated: ['stripe_customers']});
|
||||
const metadata = await model.related('stripe_customers');
|
||||
return metadata.toJSON();
|
||||
|
||||
const customers = (await models.MemberStripeCustomer.findAll({
|
||||
filter: `member_id:${member.id}`
|
||||
})).toJSON();
|
||||
|
||||
const subscriptions = await customers.reduce(async (subscriptionsPromise, customer) => {
|
||||
const customerSubscriptions = await models.StripeCustomerSubscription.findAll({
|
||||
filter: `customer_id:${customer.customer_id}`
|
||||
});
|
||||
return (await subscriptionsPromise).concat(customerSubscriptions.toJSON());
|
||||
}, []);
|
||||
|
||||
return {
|
||||
customers: customers,
|
||||
subscriptions: subscriptions
|
||||
};
|
||||
}
|
||||
|
||||
function updateMember({name}, options) {
|
||||
@ -95,6 +118,10 @@ function getStripePaymentConfig() {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!stripePaymentProcessor.config.public_token || !stripePaymentProcessor.config.secret_token) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const webhookHandlerUrl = new URL('/members/webhooks/stripe', siteUrl);
|
||||
|
||||
const checkoutSuccessUrl = new URL(siteUrl);
|
||||
@ -119,6 +146,11 @@ function getStripePaymentConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
function getRequirePaymentSetting() {
|
||||
const subscriptionSettings = settingsCache.get('members_subscription_settings');
|
||||
return !!subscriptionSettings.requirePaymentForSignup;
|
||||
}
|
||||
|
||||
module.exports = createApiInstance;
|
||||
|
||||
function createApiInstance() {
|
||||
@ -134,7 +166,8 @@ function createApiInstance() {
|
||||
signinURL.searchParams.set('token', token);
|
||||
signinURL.searchParams.set('action', type);
|
||||
return signinURL.href;
|
||||
}
|
||||
},
|
||||
allowSelfSignup: !getRequirePaymentSetting()
|
||||
},
|
||||
mail: {
|
||||
transporter: {
|
||||
@ -171,8 +204,8 @@ function createApiInstance() {
|
||||
paymentConfig: {
|
||||
stripe: getStripePaymentConfig()
|
||||
},
|
||||
setMemberMetadata,
|
||||
getMemberMetadata,
|
||||
setMetadata,
|
||||
getMetadata,
|
||||
createMember,
|
||||
updateMember,
|
||||
getMember,
|
||||
|
@ -55,7 +55,7 @@ describe('DB API', function () {
|
||||
const jsonResponse = res.body;
|
||||
should.exist(jsonResponse.db);
|
||||
jsonResponse.db.should.have.length(1);
|
||||
Object.keys(jsonResponse.db[0].data).length.should.eql(26);
|
||||
Object.keys(jsonResponse.db[0].data).length.should.eql(27);
|
||||
});
|
||||
});
|
||||
|
||||
@ -89,7 +89,6 @@ describe('DB API', function () {
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
let jsonResponse = res.body;
|
||||
let results = jsonResponse.posts;
|
||||
jsonResponse.posts.should.have.length(7);
|
||||
});
|
||||
});
|
||||
@ -104,7 +103,6 @@ describe('DB API', function () {
|
||||
.expect(200)
|
||||
.then((res) => {
|
||||
let jsonResponse = res.body;
|
||||
let results = jsonResponse.posts;
|
||||
jsonResponse.posts.should.have.length(7);
|
||||
})
|
||||
.then(() => {
|
||||
|
@ -155,7 +155,7 @@ describe('Settings API', function () {
|
||||
},
|
||||
{
|
||||
key: 'labs',
|
||||
value: '{"subscribers":false,"members":true,"default_content_visibility":"paid"}'
|
||||
value: '{"subscribers":false,"members":true}'
|
||||
}
|
||||
]
|
||||
};
|
||||
@ -221,7 +221,7 @@ describe('Settings API', function () {
|
||||
should.equal(putBody.settings[12].value, 'twitter description');
|
||||
|
||||
putBody.settings[13].key.should.eql('labs');
|
||||
should.equal(putBody.settings[13].value, '{"subscribers":false,"members":true,"default_content_visibility":"paid"}');
|
||||
should.equal(putBody.settings[13].value, '{"subscribers":false,"members":true}');
|
||||
|
||||
localUtils.API.checkResponse(putBody, 'settings');
|
||||
done();
|
||||
|
@ -4,190 +4,430 @@ const config = require('../../../../../server/config');
|
||||
const testUtils = require('../../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Settings API', function () {
|
||||
let ghostServer;
|
||||
let request;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
describe('As Owner', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
return ghostServer.stop();
|
||||
});
|
||||
after(function () {
|
||||
return ghostServer.stop();
|
||||
});
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
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();
|
||||
});
|
||||
});
|
||||
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);
|
||||
}
|
||||
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']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit permalinks', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
|
||||
};
|
||||
it('can toggle member setting', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
var jsonResponse = res.body,
|
||||
changedValue = [],
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'labs',
|
||||
value: '{"subscribers":false,"members":false}'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
|
||||
it('can\'t edit non existent setting', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Will transform "1"', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const jsonResponse = res.body,
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'is_private',
|
||||
value: '1'
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
const putBody = res.body;
|
||||
res.headers['x-cache-invalidate'].should.eql('/*');
|
||||
should.exist(putBody);
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
putBody.settings[0].key.should.eql('labs');
|
||||
putBody.settings[0].value.should.eql(JSON.stringify({subscribers: false, members: false}));
|
||||
|
||||
const putBody = res.body;
|
||||
res.headers['x-cache-invalidate'].should.eql('/*');
|
||||
should.exist(putBody);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
putBody.settings[0].key.should.eql('is_private');
|
||||
putBody.settings[0].value.should.eql(true);
|
||||
it('can\'t edit permalinks', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
|
||||
};
|
||||
|
||||
localUtils.API.checkResponse(putBody, 'settings');
|
||||
done();
|
||||
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\'t edit non existent setting', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Will transform "1"', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const jsonResponse = res.body,
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'is_private',
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const putBody = res.body;
|
||||
res.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');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Admin', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create admin
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[0].name
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function (admin) {
|
||||
request.user = admin;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot toggle member setting', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'labs',
|
||||
value: '{"subscribers":false,"members":true}'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
request.put(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send(settingToChange)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Editor', function () {
|
||||
let editor;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create editor
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[1].name
|
||||
});
|
||||
})
|
||||
.then(function (_user1) {
|
||||
editor = _user1;
|
||||
request.user = editor;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to edit settings', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Author', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create author
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[2].name
|
||||
});
|
||||
})
|
||||
.then(function (author) {
|
||||
request.user = author;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to edit settings', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -4,190 +4,430 @@ const config = require('../../../../../server/config');
|
||||
const testUtils = require('../../../../utils');
|
||||
const localUtils = require('./utils');
|
||||
const ghost = testUtils.startGhost;
|
||||
let request;
|
||||
|
||||
describe('Settings API', function () {
|
||||
let ghostServer;
|
||||
let request;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
describe('As Owner', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
after(function () {
|
||||
return ghostServer.stop();
|
||||
});
|
||||
after(function () {
|
||||
return ghostServer.stop();
|
||||
});
|
||||
|
||||
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 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 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);
|
||||
}
|
||||
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();
|
||||
});
|
||||
});
|
||||
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);
|
||||
}
|
||||
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']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
var jsonResponse = res.body;
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can\'t edit permalinks', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
|
||||
};
|
||||
it('can toggle member setting', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
var jsonResponse = res.body,
|
||||
changedValue = [],
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'labs',
|
||||
value: '{"subscribers":false,"members":false}'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
|
||||
it('can\'t edit non existent setting', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Will transform "1"', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const jsonResponse = res.body,
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'is_private',
|
||||
value: '1'
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
const putBody = res.body;
|
||||
res.headers['x-cache-invalidate'].should.eql('/*');
|
||||
should.exist(putBody);
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
putBody.settings[0].key.should.eql('labs');
|
||||
putBody.settings[0].value.should.eql(JSON.stringify({subscribers: false, members: false}));
|
||||
|
||||
const putBody = res.body;
|
||||
res.headers['x-cache-invalidate'].should.eql('/*');
|
||||
should.exist(putBody);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
putBody.settings[0].key.should.eql('is_private');
|
||||
putBody.settings[0].value.should.eql(true);
|
||||
it('can\'t edit permalinks', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [{key: 'permalinks', value: '/:primary_author/:slug/'}]
|
||||
};
|
||||
|
||||
localUtils.API.checkResponse(putBody, 'settings');
|
||||
done();
|
||||
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\'t edit non existent setting', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'testvalue', value: newValue}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('Will transform "1"', function (done) {
|
||||
request.get(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const jsonResponse = res.body,
|
||||
settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'is_private',
|
||||
value: '1'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
const putBody = res.body;
|
||||
res.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');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Admin', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create admin
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'admin+1@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[0].name
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(function (admin) {
|
||||
request.user = admin;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('cannot toggle member setting', function (done) {
|
||||
const settingToChange = {
|
||||
settings: [
|
||||
{
|
||||
key: 'labs',
|
||||
value: '{"subscribers":false,"members":true}'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
request.put(localUtils.API.getApiQuery('settings/'))
|
||||
.set('Origin', config.get('url'))
|
||||
.send(settingToChange)
|
||||
.expect('Content-Type', /json/)
|
||||
.expect('Cache-Control', testUtils.cacheRules.private)
|
||||
.expect(403)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Editor', function () {
|
||||
let editor;
|
||||
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create editor
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+1@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[1].name
|
||||
});
|
||||
})
|
||||
.then(function (_user1) {
|
||||
editor = _user1;
|
||||
request.user = editor;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to edit settings', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('As Author', function () {
|
||||
before(function () {
|
||||
return ghost()
|
||||
.then(function (_ghostServer) {
|
||||
ghostServer = _ghostServer;
|
||||
request = supertest.agent(config.get('url'));
|
||||
})
|
||||
.then(function () {
|
||||
// create author
|
||||
return testUtils.createUser({
|
||||
user: testUtils.DataGenerator.forKnex.createUser({email: 'test+2@ghost.org'}),
|
||||
role: testUtils.DataGenerator.Content.roles[2].name
|
||||
});
|
||||
})
|
||||
.then(function (author) {
|
||||
request.user = author;
|
||||
|
||||
// by default we login with the owner
|
||||
return localUtils.doAuth(request);
|
||||
});
|
||||
});
|
||||
|
||||
it('should not be able to edit settings', function (done) {
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
var jsonResponse = res.body,
|
||||
newValue = 'new value';
|
||||
should.exist(jsonResponse);
|
||||
should.exist(jsonResponse.settings);
|
||||
jsonResponse.settings = [{key: 'visibility', value: 'public'}];
|
||||
|
||||
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)
|
||||
.end(function (err, res) {
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
|
||||
jsonResponse = res.body;
|
||||
should.not.exist(res.headers['x-cache-invalidate']);
|
||||
should.exist(jsonResponse.errors);
|
||||
testUtils.API.checkResponseValue(jsonResponse.errors[0], [
|
||||
'message',
|
||||
'context',
|
||||
'type',
|
||||
'details',
|
||||
'property',
|
||||
'help',
|
||||
'code',
|
||||
'id'
|
||||
]);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -19,7 +19,7 @@ var should = require('should'),
|
||||
*/
|
||||
describe('DB version integrity', function () {
|
||||
// Only these variables should need updating
|
||||
const currentSchemaHash = 'e08c68dce141db80b1756cb911e76b89';
|
||||
const currentSchemaHash = 'bf8ffd57c6d35998dc0b17ef9d34f4cc';
|
||||
const currentFixturesHash = '9e3a7f71cab98f3fb8504d1f234b503d';
|
||||
|
||||
// If this test is failing, then it is likely a change has been made that requires a DB version bump,
|
||||
|
@ -397,6 +397,36 @@ describe('Unit: models/post: uses database (@TODO: fix me)', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if changing visibility', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sinon.stub(),
|
||||
related: sinon.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {visibility: 'public'};
|
||||
|
||||
mockPostObj.get.withArgs('visibility').returns('paid');
|
||||
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
testUtils.permissions.contributor,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
should(mockPostObj.related.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if changing author id', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sinon.stub(),
|
||||
@ -869,6 +899,36 @@ describe('Unit: models/post: uses database (@TODO: fix me)', function () {
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if changing visibility', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sinon.stub(),
|
||||
related: sinon.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {visibility: 'public'};
|
||||
|
||||
mockPostObj.get.withArgs('visibility').returns('paid');
|
||||
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
testUtils.permissions.author,
|
||||
false,
|
||||
false,
|
||||
true
|
||||
).then(() => {
|
||||
done(new Error('Permissible function should have rejected.'));
|
||||
}).catch((error) => {
|
||||
error.should.be.an.instanceof(common.errors.NoPermissionError);
|
||||
should(mockPostObj.get.called).be.false();
|
||||
should(mockPostObj.related.calledOnce).be.true();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects if editing another\'s post (using `authors`)', function (done) {
|
||||
var mockPostObj = {
|
||||
get: sinon.stub(),
|
||||
@ -1174,6 +1234,32 @@ describe('Unit: models/post: uses database (@TODO: fix me)', function () {
|
||||
should(mockPostObj.get.called).be.false();
|
||||
});
|
||||
});
|
||||
|
||||
it('resolves if changing visibility', function () {
|
||||
var mockPostObj = {
|
||||
get: sinon.stub(),
|
||||
related: sinon.stub()
|
||||
},
|
||||
context = {user: 1},
|
||||
unsafeAttrs = {visibility: 'public'};
|
||||
|
||||
mockPostObj.get.withArgs('visibility').returns('paid');
|
||||
mockPostObj.related.withArgs('authors').returns({models: [{id: 1}]});
|
||||
|
||||
models.Post.permissible(
|
||||
mockPostObj,
|
||||
'edit',
|
||||
context,
|
||||
unsafeAttrs,
|
||||
testUtils.permissions.editor,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
).then(() => {
|
||||
should(mockPostObj.get.called).be.false();
|
||||
should(mockPostObj.related.calledOnce).be.true();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -41,8 +41,8 @@
|
||||
"dependencies": {
|
||||
"@nexes/nql": "0.3.0",
|
||||
"@tryghost/helpers": "1.1.12",
|
||||
"@tryghost/members-api": "0.7.7",
|
||||
"@tryghost/members-ssr": "0.6.0",
|
||||
"@tryghost/members-api": "0.8.0",
|
||||
"@tryghost/members-ssr": "0.7.0",
|
||||
"@tryghost/social-urls": "0.1.2",
|
||||
"@tryghost/string": "^0.1.3",
|
||||
"@tryghost/url-utils": "0.6.1",
|
||||
@ -79,7 +79,7 @@
|
||||
"ghost-storage-base": "0.0.3",
|
||||
"glob": "7.1.4",
|
||||
"got": "9.6.0",
|
||||
"gscan": "2.9.0",
|
||||
"gscan": "2.10.0",
|
||||
"html-to-text": "5.1.1",
|
||||
"image-size": "0.8.3",
|
||||
"intl": "1.2.5",
|
||||
|
34
yarn.lock
34
yarn.lock
@ -227,22 +227,22 @@
|
||||
dependencies:
|
||||
"@tryghost/kg-clean-basic-html" "^0.1.3"
|
||||
|
||||
"@tryghost/magic-link@^0.2.0":
|
||||
version "0.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/magic-link/-/magic-link-0.2.0.tgz#77b6fac7d83fdff0543a1506be63601f1ec9f742"
|
||||
integrity sha512-vYj48P7RKLMfdkRCdYQ6bGv5J168ce621ZuBhEbGKf6kIiiRfNQDno+FOjiOBBKn3AS+FkIulc3z48qz/0U+Jg==
|
||||
"@tryghost/magic-link@^0.2.1":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/magic-link/-/magic-link-0.2.1.tgz#c729bf5d2fe7fa1330eccbba51ba3579834784fc"
|
||||
integrity sha512-bqlZndOXwU3b9FXvMtHIep1EradDnsfQ+4vvINQ+QsCOWKH1EDbPhjYS9f2G0xNx8BVyG4e1eMxZ5lBhJ6lBCA==
|
||||
dependencies:
|
||||
bluebird "^3.5.5"
|
||||
ghost-ignition "^3.1.0"
|
||||
jsonwebtoken "^8.5.1"
|
||||
lodash "^4.17.15"
|
||||
|
||||
"@tryghost/members-api@0.7.7":
|
||||
version "0.7.7"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.7.7.tgz#fbb08241a231dc2d651b6dacf3c4cd06e4d3799d"
|
||||
integrity sha512-a3V61Ti//PCWN3+PmfmL4MUi7ZHuWhROrr2vLfzitw3E/3CRYKXXLgk4qfe9wAVIbzdmNS8caXWXAoeVg+Vzgw==
|
||||
"@tryghost/members-api@0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/members-api/-/members-api-0.8.0.tgz#e1f0b67371b6b61f6cf4f64e5b62c92ba3777c2a"
|
||||
integrity sha512-THPd9HUyqo1WdroWFH8X+KcNfyy586dVSOYRQWIjF+URoYHmlrf2AlxBrdHMq5R8mQVCKIO0t3pmZwRnafucVA==
|
||||
dependencies:
|
||||
"@tryghost/magic-link" "^0.2.0"
|
||||
"@tryghost/magic-link" "^0.2.1"
|
||||
bluebird "^3.5.4"
|
||||
body-parser "^1.19.0"
|
||||
cookies "^0.7.3"
|
||||
@ -253,10 +253,10 @@
|
||||
node-jose "^1.1.3"
|
||||
stripe "^7.4.0"
|
||||
|
||||
"@tryghost/members-ssr@0.6.0":
|
||||
version "0.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/members-ssr/-/members-ssr-0.6.0.tgz#15a1475407a0c66b479710dffb84624038c2ad8c"
|
||||
integrity sha512-ZvZ3FuUI6F/Z3MuRMf1nNiixaSNJuYF1h5sXqUd0dIlQbCX/fOaw+57M+ApWcGGJieRvq4qnort6cZxgjQqQ6A==
|
||||
"@tryghost/members-ssr@0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@tryghost/members-ssr/-/members-ssr-0.7.0.tgz#d4e7a6554375b65efc13651361311f0c960f9cd9"
|
||||
integrity sha512-DE4xXjIvJBL/JG9wj/6tVajDgbGJ0Qvq4LjNW9gjO2EmeG6W7F9RKPyy4H4hDnSv+g5LG1oz6c/aIkivaQjNOw==
|
||||
dependencies:
|
||||
bluebird "^3.5.3"
|
||||
concat-stream "^2.0.0"
|
||||
@ -3745,10 +3745,10 @@ grunt@1.0.4:
|
||||
path-is-absolute "~1.0.0"
|
||||
rimraf "~2.6.2"
|
||||
|
||||
gscan@2.9.0:
|
||||
version "2.9.0"
|
||||
resolved "https://registry.yarnpkg.com/gscan/-/gscan-2.9.0.tgz#de169bd971043872ac830a65cd632149ecddbb8c"
|
||||
integrity sha512-igE0rPtbc0u3IQ3pruFXTSarc+JIHEXbRv/iyqeFlujgR0iKJ0tKPKRQGceYqzy5x+sT7MTwtxpa+LLOhN2SNw==
|
||||
gscan@2.10.0:
|
||||
version "2.10.0"
|
||||
resolved "https://registry.yarnpkg.com/gscan/-/gscan-2.10.0.tgz#62c5a4e685304335e716baf9ec26a3974a158e07"
|
||||
integrity sha512-Z0R0hEk00L/dfN0FvdGVA1XnE32/kYMFL9fkkpvlRYw9Rwwftp5sEtUAOrxuUA1ysersb9wLlWgunaf4BIzcwA==
|
||||
dependencies:
|
||||
"@tryghost/extract-zip" "1.6.6"
|
||||
"@tryghost/pretty-cli" "1.2.1"
|
||||
|
Loading…
Reference in New Issue
Block a user