1
0
Fork 0
mirror of https://github.com/TryGhost/Ghost-Admin.git synced 2023-12-14 02:33:04 +01:00

Fixed "Authorization Failed" error screens when not logged in

no issue
- `/config/` can only be requested when authenticated
- updated `/config/` mock to look for an Authentication header and return a 403 if it's missing
- updated `ajax` service to add an `Authentication` header when authenticated in testing env (cookies are not present when testing)
- updated `config` service to add `fetchUnauthenticated()` and `fetchAuthenticated()` methods in addition to `.fetch()`
- updated `application` route to only fetch authenticated config when authenticated
- updated `signin` controller to correctly fetch config after sign-in
This commit is contained in:
Kevin Ansfield 2019-02-26 10:38:00 +07:00
parent 17854c6c81
commit 4ab77cbb21
5 changed files with 59 additions and 18 deletions

View file

@ -45,7 +45,7 @@ export default Controller.extend(ValidationEngine, {
let promises = [];
promises.pushObject(this.get('settings').fetch());
promises.pushObject(this.get('config').fetchPrivate());
promises.pushObject(this.get('config').fetchAuthenticated());
// fetch settings and private config for synchronous access
yield RSVP.all(promises);

View file

@ -43,7 +43,7 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
routeAfterAuthentication: 'posts',
beforeModel() {
return this.get('config').fetch();
return this.get('config').fetchUnauthenticated();
},
afterModel(model, transition) {
@ -53,6 +53,7 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
this.set('appLoadTransition', transition);
transition.send('loadServerNotifications');
let configPromise = this.get('config').fetchAuthenticated();
let featurePromise = this.get('feature').fetch();
let settingsPromise = this.get('settings').fetch();
let tourPromise = this.get('tour').fetchViewed();
@ -60,6 +61,7 @@ export default Route.extend(ApplicationRouteMixin, ShortcutsRoute, {
// return the feature/settings load promises so that we block until
// they are loaded to enable synchronous access everywhere
return RSVP.all([
configPromise,
featurePromise,
settingsPromise,
tourPromise

View file

@ -118,6 +118,8 @@ export function isThemeValidationError(errorOrStatus, payload) {
let ajaxService = AjaxService.extend({
session: service(),
isTesting: undefined,
// flag to tell our ESA authenticator not to try an invalidate DELETE request
// because it's been triggered by this service's 401 handling which means the
// DELETE would fail and get stuck in an infinite loop
@ -130,9 +132,20 @@ let ajaxService = AjaxService.extend({
headers['X-Ghost-Version'] = config.APP.version;
headers['App-Pragma'] = 'no-cache';
if (this.session.isAuthenticated && this.isTesting) {
headers.Authorization = 'Test';
}
return headers;
}).volatile(),
init() {
this._super(...arguments);
if (this.isTesting === undefined) {
this.isTesting = config.environment === 'test';
}
},
// ember-ajax recognises `application/vnd.api+json` as a JSON-API request
// and formats appropriately, we want to handle `application/json` the same
_makeRequest(hash) {

View file

@ -9,6 +9,7 @@ const {_ProxyMixin} = Ember;
export default Service.extend(_ProxyMixin, {
ajax: service(),
ghostPaths: service(),
session: service(),
content: null,
@ -18,23 +19,37 @@ export default Service.extend(_ProxyMixin, {
},
fetch() {
let promises = [];
promises.push(this.fetchUnauthenticated());
if (this.session.isAuthenticated) {
promises.push(this.fetchAuthenticated());
}
return RSVP.all(promises);
},
fetchUnauthenticated() {
let siteUrl = this.ghostPaths.url.api('site');
return this.ajax.request(siteUrl).then(({site}) => {
// normalize url to non-trailing-slash
site.blogUrl = site.url.replace(/\/$/, '');
site.blogTitle = site.title;
delete site.url;
delete site.title;
Object.assign(this.content, site);
}).then(() => {
this.notifyPropertyChange('content');
});
},
fetchAuthenticated() {
let configUrl = this.ghostPaths.url.api('config');
return RSVP.all([
this.ajax.request(siteUrl).then(({site}) => {
// normalize url to non-trailing-slash
site.blogUrl = site.url.replace(/\/$/, '');
site.blogTitle = site.title;
delete site.url;
delete site.title;
Object.assign(this.content, site);
}),
this.ajax.request(configUrl).then(({config}) => {
Object.assign(this.content, config);
})
]).then(() => {
return this.ajax.request(configUrl).then(({config}) => {
Object.assign(this.content, config);
}).then(() => {
this.notifyPropertyChange('content');
});
},

View file

@ -1,7 +1,18 @@
import {Response} from 'ember-cli-mirage';
import {isEmpty} from '@ember/utils';
export default function mockConfig(server) {
server.get('/config/', function ({db}) {
server.get('/config/', function ({db}, request) {
if (!request.requestHeaders.Authorization) {
return new Response(403, {}, {
errors: [{
type: 'NoPermissionError',
message: 'Authorization failed',
context: 'Unable to determine the authenticated user or integration. Check that cookies are being passed through if using session authentication.'
}]
});
}
if (isEmpty(db.configs)) {
server.loadFixtures('configs');
}