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  }