e34c16a90c
* ui: Logout button This commit adds an easier way to logout of the UI using a logout button Notes: - Added a Logout button to the main navigation when you are logged in, meaning you have easy access to a way to log out of the UI. - Changed all wording to use 'Log in/out' vocabulary instad of 'stop using'. - The logout button opens a panel to show you your current ACL token and a logout button in order to logout. - When using legacy ACLs we don't show the current ACL token as legacy ACLs tokens only have secret values, whereas the new ACLs use a non-secret ID plus a secret ID (that we don't show). - We also added a new `<EmptyState />` component to use for all our empty states. We currently only use this for the ACLs disabled screen to provide more outgoing links to more readind material/documentation to help you to understand and enable ACLs. - The `<DataSink />` component is the sibling to our `<DataSource />` component and whilst is much simpler (as it doesn't require polling support), its tries to use the same code patterns for consistencies sake. - We had a fun problem with ember-data's `store.unloadAll` here, and in the end went with `store.init` to empty the ember-data store instead due to timing issues. - We've tried to use already existing patterns in the Consul UI here such as our preexisting `feedback` service, although these are likely to change in the future. The thinking here is to add this feature with as little change as possible. Overall this is a precursor to a much larger piece of work centered on auth in the UI. We figured this was a feature complete piece of work as it is and thought it was worthwhile to PR as a feature on its own, which also means the larger piece of work will be a smaller scoped PR also.
114 lines
4.1 KiB
JavaScript
114 lines
4.1 KiB
JavaScript
import Route from '@ember/routing/route';
|
|
import { inject as service } from '@ember/service';
|
|
import { hash } from 'rsvp';
|
|
import { next } from '@ember/runloop';
|
|
|
|
import WithBlockingActions from 'consul-ui/mixins/with-blocking-actions';
|
|
|
|
const removeLoading = function($from) {
|
|
return $from.classList.remove('ember-loading');
|
|
};
|
|
export default Route.extend(WithBlockingActions, {
|
|
dom: service('dom'),
|
|
nspacesRepo: service('repository/nspace/disabled'),
|
|
repo: service('repository/dc'),
|
|
settings: service('settings'),
|
|
actions: {
|
|
loading: function(transition, originRoute) {
|
|
const $root = this.dom.root();
|
|
let dc = null;
|
|
if (originRoute.routeName !== 'dc') {
|
|
const model = this.modelFor('dc') || { dcs: null, dc: { Name: null } };
|
|
dc = this.repo.getActive(model.dc.Name, model.dcs);
|
|
}
|
|
hash({
|
|
loading: !$root.classList.contains('ember-loading'),
|
|
dc: dc,
|
|
nspace: this.nspacesRepo.getActive(),
|
|
}).then(model => {
|
|
next(() => {
|
|
const controller = this.controllerFor('application');
|
|
controller.setProperties(model);
|
|
transition.promise.finally(function() {
|
|
removeLoading($root);
|
|
controller.setProperties({
|
|
loading: false,
|
|
dc: model.dc,
|
|
});
|
|
});
|
|
});
|
|
});
|
|
return true;
|
|
},
|
|
error: function(e, transition) {
|
|
// TODO: Normalize all this better
|
|
let error = {
|
|
status: e.code || '',
|
|
message: e.message || e.detail || 'Error',
|
|
};
|
|
if (e.errors && e.errors[0]) {
|
|
error = e.errors[0];
|
|
error.message = error.title || error.detail || 'Error';
|
|
}
|
|
// Try and get the currently attempted dc, whereever that may be
|
|
const model = this.modelFor('dc') || this.modelFor('nspace.dc');
|
|
// TODO: Unfortunately ember will not maintain the correct URL
|
|
// for you i.e. when this happens the URL in your browser location bar
|
|
// will be the URL where you clicked on the link to come here
|
|
// not the URL where you got the 403 response
|
|
// Currently this is dealt with a lot better with the new ACLs system, in that
|
|
// if you get a 403 in the ACLs area, the URL is correct
|
|
// Moving that app wide right now wouldn't be ideal, therefore simply redirect
|
|
// to the ACLs URL instead of maintaining the actual URL, which is better than the old
|
|
// 403 page
|
|
// To note: Consul only gives you back a 403 if a non-existent token has been sent in the header
|
|
// if a token has not been sent at all, it just gives you a 200 with an empty dataset
|
|
// We set a completely null token here, which is different to just deleting a token
|
|
// in that deleting a token means 'logout' whereas setting it to completely null means
|
|
// there was a 403. This is only required to get around the legacy tokens
|
|
// a lot of this can go once we don't support legacy tokens
|
|
if (error.status === '403') {
|
|
return this.settings.persist({
|
|
token: {
|
|
AccessorID: null,
|
|
SecretID: null,
|
|
Namespace: null,
|
|
},
|
|
});
|
|
}
|
|
if (error.status === '') {
|
|
error.message = 'Error';
|
|
}
|
|
const $root = this.dom.root();
|
|
hash({
|
|
error: error,
|
|
nspace: this.nspacesRepo.getActive(),
|
|
dc:
|
|
error.status.toString().indexOf('5') !== 0
|
|
? this.repo.getActive()
|
|
: model && model.dc
|
|
? model.dc
|
|
: { Name: 'Error' },
|
|
dcs: model && model.dcs ? model.dcs : [],
|
|
})
|
|
.then(model => Promise.all([model, this.repo.clearActive()]))
|
|
.then(([model]) => {
|
|
removeLoading($root);
|
|
model.nspaces = [model.nspace];
|
|
// we can't use setupController as we received an error
|
|
// so we do it manually instead
|
|
next(() => {
|
|
this.controllerFor('error').setProperties(model);
|
|
});
|
|
})
|
|
.catch(e => {
|
|
removeLoading($root);
|
|
next(() => {
|
|
this.controllerFor('error').setProperties({ error: error });
|
|
});
|
|
});
|
|
return true;
|
|
},
|
|
},
|
|
});
|