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