open-vault/ui/app/routes/vault/cluster.js
Noelle Daley 58dfd8aa68
Add Policy-based Navigation (#5967)
* add permissions service

* start template helper

* match prefixed paths

* gate sidebar links

* land on first page user has access to

* show nav when user first logs in

* clear paths when user logs out

* add tests

* implement feedback

* show all nav items if no policy is found

* update onboarding wizard

* fix some unrelated tests

* add support for namespaces

* gate wizard

* unstage package and lockfile
2019-01-18 14:04:40 -08:00

119 lines
3.3 KiB
JavaScript

import { inject as service } from '@ember/service';
import { computed } from '@ember/object';
import { reject } from 'rsvp';
import Route from '@ember/routing/route';
import { getOwner } from '@ember/application';
import { task, timeout } from 'ember-concurrency';
import Ember from 'ember';
import ClusterRoute from 'vault/mixins/cluster-route';
import ModelBoundaryRoute from 'vault/mixins/model-boundary-route';
const POLL_INTERVAL_MS = 10000;
export default Route.extend(ModelBoundaryRoute, ClusterRoute, {
namespaceService: service('namespace'),
version: service(),
permissions: service(),
store: service(),
auth: service(),
currentCluster: service(),
modelTypes: computed(function() {
return ['node', 'secret', 'secret-engine'];
}),
globalNamespaceModels: computed(function() {
return ['node', 'cluster'];
}),
queryParams: {
namespaceQueryParam: {
refreshModel: true,
},
},
getClusterId(params) {
const { cluster_name } = params;
const cluster = this.modelFor('vault').findBy('name', cluster_name);
return cluster ? cluster.get('id') : null;
},
clearNonGlobalModels() {
// this method clears all of the ember data cached models except
// the model types blacklisted in `globalNamespaceModels`
let store = this.store;
let modelsToKeep = this.get('globalNamespaceModels');
for (let model of getOwner(this)
.lookup('data-adapter:main')
.getModelTypes()) {
let { name } = model;
if (modelsToKeep.includes(name)) {
return;
}
store.unloadAll(name);
}
},
beforeModel() {
const params = this.paramsFor(this.routeName);
this.clearNonGlobalModels();
this.get('namespaceService').setNamespace(params.namespaceQueryParam);
const id = this.getClusterId(params);
if (id) {
this.get('auth').setCluster(id);
this.get('permissions').getPaths.perform();
return this.get('version').fetchFeatures();
} else {
return reject({ httpStatus: 404, message: 'not found', path: params.cluster_name });
}
},
model(params) {
const id = this.getClusterId(params);
return this.get('store').findRecord('cluster', id);
},
poll: task(function*() {
while (true) {
// when testing, the polling loop causes promises to never settle so acceptance tests hang
// to get around that, we just disable the poll in tests
if (Ember.testing) {
return;
}
yield timeout(POLL_INTERVAL_MS);
try {
yield this.controller.model.reload();
yield this.transitionToTargetRoute();
} catch (e) {
// we want to keep polling here
}
}
})
.cancelOn('deactivate')
.keepLatest(),
afterModel(model) {
this._super(...arguments);
this.get('currentCluster').setCluster(model);
// Check that namespaces is enabled and if not,
// clear the namespace by transition to this route w/o it
if (this.get('namespaceService.path') && !this.get('version.hasNamespaces')) {
return this.transitionTo(this.routeName, { queryParams: { namespace: '' } });
}
return this.transitionToTargetRoute();
},
setupController() {
this._super(...arguments);
this.poll.perform();
},
actions: {
error(e) {
if (e.httpStatus === 503 && e.errors[0] === 'Vault is sealed') {
this.refresh();
}
return true;
},
},
});