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  }