github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/ui/app/abilities/abstract.js (about) 1 import { Ability } from 'ember-can'; 2 import { inject as service } from '@ember/service'; 3 import { computed, get } from '@ember/object'; 4 import { equal, not } from '@ember/object/computed'; 5 import classic from 'ember-classic-decorator'; 6 7 @classic 8 export default class Abstract extends Ability { 9 @service system; 10 @service token; 11 12 @not('token.aclEnabled') bypassAuthorization; 13 @equal('token.selfToken.type', 'management') selfTokenIsManagement; 14 15 // Pass in a namespace to `can` or `cannot` calls to override 16 // https://github.com/minutebase/ember-can#additional-attributes 17 namespace = 'default'; 18 19 get _namespace() { 20 if (!this.namespace) return 'default'; 21 if (typeof this.namespace === 'string') return this.namespace; 22 return get(this.namespace, 'name'); 23 } 24 25 @computed('_namespace', 'token.selfTokenPolicies.[]') 26 get rulesForNamespace() { 27 let namespace = this._namespace; 28 29 return (this.get('token.selfTokenPolicies') || []) 30 .toArray() 31 .reduce((rules, policy) => { 32 let policyNamespaces = get(policy, 'rulesJSON.Namespaces') || []; 33 34 let matchingNamespace = this._findMatchingNamespace( 35 policyNamespaces, 36 namespace 37 ); 38 39 if (matchingNamespace) { 40 rules.push( 41 policyNamespaces.find( 42 (namespace) => namespace.Name === matchingNamespace 43 ) 44 ); 45 } 46 47 return rules; 48 }, []); 49 } 50 51 @computed('token.selfTokenPolicies.[]') 52 get capabilitiesForAllNamespaces() { 53 return (this.get('token.selfTokenPolicies') || []) 54 .toArray() 55 .reduce((allCapabilities, policy) => { 56 (get(policy, 'rulesJSON.Namespaces') || []).forEach( 57 ({ Capabilities }) => { 58 allCapabilities = allCapabilities.concat(Capabilities); 59 } 60 ); 61 return allCapabilities; 62 }, []); 63 } 64 65 namespaceIncludesCapability(capability) { 66 return this.rulesForNamespace.some((rules) => { 67 let capabilities = get(rules, 'Capabilities') || []; 68 return capabilities.includes(capability); 69 }); 70 } 71 72 @computed('system.features.[]') 73 get features() { 74 return this.system.features; 75 } 76 77 featureIsPresent(featureName) { 78 return this.features.includes(featureName); 79 } 80 81 // Chooses the closest namespace as described at the bottom here: 82 // https://learn.hashicorp.com/tutorials/nomad/access-control-policies?in=nomad/access-control#namespace-rules 83 _findMatchingNamespace(policyNamespaces, namespace) { 84 let namespaceNames = policyNamespaces.mapBy('Name'); 85 86 if (namespaceNames.includes(namespace)) { 87 return namespace; 88 } 89 90 let globNamespaceNames = namespaceNames.filter((namespaceName) => 91 namespaceName.includes('*') 92 ); 93 94 let matchingNamespaceName = globNamespaceNames.reduce( 95 (mostMatching, namespaceName) => { 96 // Convert * wildcards to .* for regex matching 97 let namespaceNameRegExp = new RegExp( 98 namespaceName.replace(/\*/g, '.*') 99 ); 100 let characterDifference = namespace.length - namespaceName.length; 101 102 if ( 103 characterDifference < mostMatching.mostMatchingCharacterDifference && 104 namespace.match(namespaceNameRegExp) 105 ) { 106 return { 107 mostMatchingNamespaceName: namespaceName, 108 mostMatchingCharacterDifference: characterDifference, 109 }; 110 } else { 111 return mostMatching; 112 } 113 }, 114 { 115 mostMatchingNamespaceName: null, 116 mostMatchingCharacterDifference: Number.MAX_SAFE_INTEGER, 117 } 118 ).mostMatchingNamespaceName; 119 120 if (matchingNamespaceName) { 121 return matchingNamespaceName; 122 } else if (namespaceNames.includes('default')) { 123 return 'default'; 124 } 125 } 126 }