104 lines
3.4 KiB
JavaScript
104 lines
3.4 KiB
JavaScript
import { Ability } from 'ember-can';
|
|
import { inject as service } from '@ember/service';
|
|
import { computed, get } from '@ember/object';
|
|
import { equal, not } from '@ember/object/computed';
|
|
import classic from 'ember-classic-decorator';
|
|
|
|
@classic
|
|
export default class Abstract extends Ability {
|
|
@service system;
|
|
@service token;
|
|
|
|
@not('token.aclEnabled') bypassAuthorization;
|
|
@equal('token.selfToken.type', 'management') selfTokenIsManagement;
|
|
|
|
@computed('system.activeNamespace.name')
|
|
get activeNamespace() {
|
|
return this.get('system.activeNamespace.name') || 'default';
|
|
}
|
|
|
|
@computed('activeNamespace', 'token.selfTokenPolicies.[]')
|
|
get rulesForActiveNamespace() {
|
|
let activeNamespace = this.activeNamespace;
|
|
|
|
return (this.get('token.selfTokenPolicies') || []).toArray().reduce((rules, policy) => {
|
|
let policyNamespaces = get(policy, 'rulesJSON.Namespaces') || [];
|
|
|
|
let matchingNamespace = this._findMatchingNamespace(policyNamespaces, activeNamespace);
|
|
|
|
if (matchingNamespace) {
|
|
rules.push(policyNamespaces.find(namespace => namespace.Name === matchingNamespace));
|
|
}
|
|
|
|
return rules;
|
|
}, []);
|
|
}
|
|
|
|
@computed('token.selfTokenPolicies.[]')
|
|
get capabilitiesForAllNamespaces() {
|
|
return (this.get('token.selfTokenPolicies') || [])
|
|
.toArray()
|
|
.reduce((allCapabilities, policy) => {
|
|
(get(policy, 'rulesJSON.Namespaces') || []).forEach(({ Capabilities }) => {
|
|
allCapabilities = allCapabilities.concat(Capabilities);
|
|
});
|
|
return allCapabilities;
|
|
}, []);
|
|
}
|
|
|
|
activeNamespaceIncludesCapability(capability) {
|
|
return this.rulesForActiveNamespace.some(rules => {
|
|
let capabilities = get(rules, 'Capabilities') || [];
|
|
return capabilities.includes(capability);
|
|
});
|
|
}
|
|
|
|
@computed('system.features.[]')
|
|
get features() {
|
|
return this.system.features;
|
|
}
|
|
|
|
featureIsPresent(featureName) {
|
|
return this.features.includes(featureName);
|
|
}
|
|
|
|
// Chooses the closest namespace as described at the bottom here:
|
|
// https://learn.hashicorp.com/tutorials/nomad/access-control-policies?in=nomad/access-control#namespace-rules
|
|
_findMatchingNamespace(policyNamespaces, activeNamespace) {
|
|
let namespaceNames = policyNamespaces.mapBy('Name');
|
|
|
|
if (namespaceNames.includes(activeNamespace)) {
|
|
return activeNamespace;
|
|
}
|
|
|
|
let globNamespaceNames = namespaceNames.filter(namespaceName => namespaceName.includes('*'));
|
|
|
|
let matchingNamespaceName = globNamespaceNames.reduce(
|
|
(mostMatching, namespaceName) => {
|
|
// Convert * wildcards to .* for regex matching
|
|
let namespaceNameRegExp = new RegExp(namespaceName.replace(/\*/g, '.*'));
|
|
let characterDifference = activeNamespace.length - namespaceName.length;
|
|
|
|
if (
|
|
characterDifference < mostMatching.mostMatchingCharacterDifference &&
|
|
activeNamespace.match(namespaceNameRegExp)
|
|
) {
|
|
return {
|
|
mostMatchingNamespaceName: namespaceName,
|
|
mostMatchingCharacterDifference: characterDifference,
|
|
};
|
|
} else {
|
|
return mostMatching;
|
|
}
|
|
},
|
|
{ mostMatchingNamespaceName: null, mostMatchingCharacterDifference: Number.MAX_SAFE_INTEGER }
|
|
).mostMatchingNamespaceName;
|
|
|
|
if (matchingNamespaceName) {
|
|
return matchingNamespaceName;
|
|
} else if (namespaceNames.includes('default')) {
|
|
return 'default';
|
|
}
|
|
}
|
|
}
|