From 468e16c52f3ddc0adae1f70bd72e5ede2987f070 Mon Sep 17 00:00:00 2001 From: Jai <41024828+ChaiWithJai@users.noreply.github.com> Date: Fri, 5 Aug 2022 16:40:22 -0400 Subject: [PATCH] refact: namespace glob matching (#14037) * refact: allow namespace glob matching * test: namespace glob matching --- ui/app/abilities/variable.js | 14 ++++- ui/tests/unit/abilities/variable-test.js | 71 ++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 2 deletions(-) diff --git a/ui/app/abilities/variable.js b/ui/app/abilities/variable.js index 94148eff3..40f77fe5b 100644 --- a/ui/app/abilities/variable.js +++ b/ui/app/abilities/variable.js @@ -105,9 +105,13 @@ export default class Variable extends AbstractAbility { return (get(this, 'token.selfTokenPolicies') || []) .toArray() .reduce((paths, policy) => { - const matchingNamespace = this.namespace ?? 'default'; + const namespaces = get(policy, 'rulesJSON.Namespaces'); + const matchingNamespace = this._nearestMatchingNamespace( + namespaces, + this.namespace + ); - const variables = (get(policy, 'rulesJSON.Namespaces') || []).find( + const variables = (namespaces || []).find( (namespace) => namespace.Name === matchingNamespace )?.SecureVariables; @@ -124,6 +128,12 @@ export default class Variable extends AbstractAbility { }, []); } + _nearestMatchingNamespace(policyNamespaces, namespace) { + if (!namespace || !policyNamespaces) return 'default'; + + return this._findMatchingNamespace(policyNamespaces, namespace); + } + _formatMatchingPathRegEx(path, wildCardPlacement = 'end') { const replacer = () => '\\/'; if (wildCardPlacement === 'end') { diff --git a/ui/tests/unit/abilities/variable-test.js b/ui/tests/unit/abilities/variable-test.js index 742c62db0..89335b810 100644 --- a/ui/tests/unit/abilities/variable-test.js +++ b/ui/tests/unit/abilities/variable-test.js @@ -1069,5 +1069,76 @@ module('Unit | Ability | variable', function (hooks) { 'It should return the exact path match.' ); }); + + test('it handles globs in namespaces', function (assert) { + const mockToken = Service.extend({ + aclEnabled: true, + selfToken: { type: 'client' }, + selfTokenPolicies: [ + { + rulesJSON: { + Namespaces: [ + { + Name: '*', + Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'], + SecureVariables: { + Paths: [ + { + Capabilities: ['list'], + PathSpec: '*', + }, + ], + }, + }, + { + Name: 'namespace-1', + Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'], + SecureVariables: { + Paths: [ + { + Capabilities: ['list', 'read', 'destroy', 'create'], + PathSpec: '*', + }, + ], + }, + }, + { + Name: 'namespace-2', + Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'], + SecureVariables: { + Paths: [ + { + Capabilities: ['list', 'read', 'destroy', 'create'], + PathSpec: 'blue/*', + }, + { + Capabilities: ['list', 'read', 'create'], + PathSpec: 'nomad/jobs/*', + }, + ], + }, + }, + ], + }, + }, + ], + }); + + this.owner.register('service:token', mockToken); + this.ability.namespace = 'pablo'; + + const allPaths = this.ability.allPaths; + + assert.deepEqual( + allPaths, + [ + { + capabilities: ['list'], + name: '*', + }, + ], + 'It should return the glob matching namespace match.' + ); + }); }); });