open-vault/ui/app/services/permissions.js
Angel Garbarino db053601e5
Core Usage Metrics (#8347)
* Core usage metrics v1 (merge to side-branch) (#8238)

* restructure menu layout per designs

* setup new routing that will set the stage for a metrics landing page

* fix formatting

* Revert "fix formatting"

This reverts commit e77cdec5e58cdcea49aa1b97f80238433c4f7d1e.

* fix formatting

* small styling changes

* change request routing to metrics

* rename route js file

* Core usage metrics v2 (#8263)

* restructure menu layout per designs

* setup new routing that will set the stage for a metrics landing page

* fix formatting

* Revert "fix formatting"

This reverts commit e77cdec5e58cdcea49aa1b97f80238433c4f7d1e.

* fix formatting

* small styling changes

* change request routing to metrics

* rename route js file

* setup selectable card component and api request

* add token and http request models to route and template

* add entities to route and template

* clean up

* add breadcrumbs and some clean up work

* remove unused selectable-card component

* refactor to a serializer

* move adapters, serializers, and models into metrics folder

* remove unused file

* address pr comments

* address pr comments

* Core Usage Metrics V3 (#8316)

* restructure menu layout per designs

* setup new routing that will set the stage for a metrics landing page

* fix formatting

* Revert "fix formatting"

This reverts commit e77cdec5e58cdcea49aa1b97f80238433c4f7d1e.

* fix formatting

* small styling changes

* change request routing to metrics

* rename route js file

* setup selectable card component and api request

* add token and http request models to route and template

* add entities to route and template

* clean up

* add breadcrumbs and some clean up work

* remove unused selectable-card component

* setup smaller http request bar chart

* refactor to a serializer

* move adapters, serializers, and models into metrics folder

* remove unused file

* setup change part of component

* fix broken model

* add conditional class

* setting up computed properties in new component

* small fixes

* setup components

* minor fixes

* rename

* clean up

* firefox fix

* remove shadow bars

* move out of metrics folders

* modify permissions to show difference between token entities and requests

* make tests

* fix class names and associated tests

* clean up

* fix text overflow in non-chrome browsers

* address pr comments, specifically class names and tests

* move into one component

* clean up component descriptions in comments

* small wording changes

* fix for accessibility

* address pr comments around component examples for storybook

* fix test

* fix failing test

* fix test
2020-02-13 12:44:57 -07:00

173 lines
4.8 KiB
JavaScript

import Service, { inject as service } from '@ember/service';
import { task } from 'ember-concurrency';
const API_PATHS = {
access: {
methods: 'sys/auth',
entities: 'identity/entity/id',
groups: 'identity/group/id',
leases: 'sys/leases/lookup',
namespaces: 'sys/namespaces',
'control-groups': 'sys/control-group/',
},
policies: {
acl: 'sys/policies/acl',
rgp: 'sys/policies/rgp',
egp: 'sys/policies/egp',
},
tools: {
wrap: 'sys/wrapping/wrap',
lookup: 'sys/wrapping/lookup',
unwrap: 'sys/wrapping/unwrap',
rewrap: 'sys/wrapping/rewrap',
random: 'sys/tools/random',
hash: 'sys/tools/hash',
},
status: {
replication: 'sys/replication',
license: 'sys/license',
seal: 'sys/seal',
raft: 'sys/storage/raft/configuration',
},
metrics: {
dashboard: 'sys/internal/counters',
requests: 'sys/internal/counters/requests',
},
};
const API_PATHS_TO_ROUTE_PARAMS = {
'sys/auth': ['vault.cluster.access.methods'],
'identity/entity/id': ['vault.cluster.access.identity', 'entities'],
'identity/group/id': ['vault.cluster.access.identity', 'groups'],
'sys/leases/lookup': ['vault.cluster.access.leases'],
'sys/namespaces': ['vault.cluster.access.namespaces'],
'sys/control-group/': ['vault.cluster.access.control-groups'],
};
/*
The Permissions service is used to gate top navigation and sidebar items.
It fetches a users' policy from the resultant-acl endpoint and stores their
allowed exact and glob paths as state. It also has methods for checking whether
a user has permission for a given path.
*/
export default Service.extend({
exactPaths: null,
globPaths: null,
canViewAll: null,
store: service(),
auth: service(),
namespace: service(),
getPaths: task(function*() {
if (this.paths) {
return;
}
try {
let resp = yield this.get('store')
.adapterFor('permissions')
.query();
this.setPaths(resp);
return;
} catch (err) {
// If no policy can be found, default to showing all nav items.
this.set('canViewAll', true);
}
}),
setPaths(resp) {
this.set('exactPaths', resp.data.exact_paths);
this.set('globPaths', resp.data.glob_paths);
this.set('canViewAll', resp.data.root);
},
reset() {
this.set('exactPaths', null);
this.set('globPaths', null);
this.set('canViewAll', null);
},
hasNavPermission(navItem, routeParams) {
if (routeParams) {
// viewing the entity and groups pages require the list capability, while the others require the default, which is anything other than deny
let capability = routeParams === 'entities' || routeParams === 'groups' ? ['list'] : [null];
return this.hasPermission(API_PATHS[navItem][routeParams], capability);
}
return Object.values(API_PATHS[navItem]).some(path => this.hasPermission(path));
},
navPathParams(navItem) {
const path = Object.values(API_PATHS[navItem]).find(path => this.hasPermission(path));
if (['policies', 'tools'].includes(navItem)) {
return path.split('/').lastObject;
}
return API_PATHS_TO_ROUTE_PARAMS[path];
},
pathNameWithNamespace(pathName) {
const namespace = this.get('namespace').path;
if (namespace) {
return `${namespace}/${pathName}`;
} else {
return pathName;
}
},
hasPermission(pathName, capabilities = [null]) {
const path = this.pathNameWithNamespace(pathName);
if (this.canViewAll) {
return true;
}
return capabilities.every(
capability => this.hasMatchingExactPath(path, capability) || this.hasMatchingGlobPath(path, capability)
);
},
hasMatchingExactPath(pathName, capability) {
const exactPaths = this.get('exactPaths');
if (exactPaths) {
const prefix = Object.keys(exactPaths).find(path => path.startsWith(pathName));
const hasMatchingPath = prefix && !this.isDenied(exactPaths[prefix]);
if (prefix && capability) {
return this.hasCapability(exactPaths[prefix], capability) && hasMatchingPath;
}
return hasMatchingPath;
}
return false;
},
hasMatchingGlobPath(pathName, capability) {
const globPaths = this.get('globPaths');
if (globPaths) {
const matchingPath = Object.keys(globPaths).find(k => {
return pathName.includes(k) || pathName.includes(k.replace(/\/$/, ''));
});
const hasMatchingPath =
(matchingPath && !this.isDenied(globPaths[matchingPath])) ||
Object.prototype.hasOwnProperty.call(globPaths, '');
if (matchingPath && capability) {
return this.hasCapability(globPaths[matchingPath], capability) && hasMatchingPath;
}
return hasMatchingPath;
}
return false;
},
hasCapability(path, capability) {
return path.capabilities.includes(capability);
},
isDenied(path) {
return path.capabilities.includes('deny');
},
});