mirror of
https://github.com/kemko/nomad.git
synced 2026-01-01 16:05:42 +03:00
* Modify variable access permissions for UI users with write in only certain namespaces * Addressing some PR comments * Variables index namespaces on * and ability checks are now namespaced * Mistook Delete for Destroy, and update unit tests for mult-return allPaths
971 lines
26 KiB
JavaScript
971 lines
26 KiB
JavaScript
/**
|
|
* Copyright (c) HashiCorp, Inc.
|
|
* SPDX-License-Identifier: BUSL-1.1
|
|
*/
|
|
|
|
/* eslint-disable ember/avoid-leaking-state-in-ember-objects */
|
|
import { module, test } from 'qunit';
|
|
import { setupTest } from 'ember-qunit';
|
|
import Service from '@ember/service';
|
|
import setupAbility from 'nomad-ui/tests/helpers/setup-ability';
|
|
|
|
module('Unit | Ability | variable', function (hooks) {
|
|
setupTest(hooks);
|
|
setupAbility('variable')(hooks);
|
|
hooks.beforeEach(function () {
|
|
const mockSystem = Service.extend({
|
|
features: [],
|
|
});
|
|
|
|
this.owner.register('service:system', mockSystem);
|
|
});
|
|
|
|
module('#list', function () {
|
|
test('it does not permit listing variables by default', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it does not permit listing variables when token type is client', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it permits listing variables when token type is management', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'management' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canList);
|
|
});
|
|
|
|
test('it permits listing variables when token has Variables with list capabilities in its rules', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['list'], PathSpec: '*' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canList);
|
|
});
|
|
|
|
test('it does not permit listing variables when token has Variables alone in its rules', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it does not permit listing variables when token has a null Variables block', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: null,
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it does not permit listing variables when token has a Variables block where paths are without capabilities', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: [], PathSpec: '*' },
|
|
{ Capabilities: [], PathSpec: 'foo' },
|
|
{ Capabilities: [], PathSpec: 'foo/bar' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it does not permit listing variables when token has no Variables block', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canList);
|
|
});
|
|
|
|
test('it permits listing variables when token multiple namespaces, only one of which having a Variables block', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: null,
|
|
},
|
|
{
|
|
Name: 'nonsense',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: [], PathSpec: '*' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'shenanigans',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['list'], PathSpec: 'foo/bar/baz' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canList);
|
|
});
|
|
});
|
|
|
|
module('#create', function () {
|
|
test('it does not permit creating variables by default', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canWrite);
|
|
});
|
|
|
|
test('it permits creating variables when token type is management', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'management' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canWrite);
|
|
});
|
|
|
|
test('it permits creating variables when acl is disabled', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: false,
|
|
selfToken: { type: 'client' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canWrite);
|
|
});
|
|
|
|
test('it permits creating variables when token has Variables with write capabilities in its rules', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: '*' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canWrite);
|
|
});
|
|
|
|
test('it handles namespace matching', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'pablo',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
this.ability.path = 'foo/bar';
|
|
this.ability.namespace = 'pablo';
|
|
|
|
assert.ok(this.ability.canWrite);
|
|
});
|
|
});
|
|
|
|
module('#destroy', function () {
|
|
test('it does not permit destroying variables by default', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canDestroy);
|
|
});
|
|
|
|
test('it permits destroying variables when token type is management', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'management' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canDestroy);
|
|
});
|
|
|
|
test('it permits destroying variables when acl is disabled', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: false,
|
|
selfToken: { type: 'client' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canDestroy);
|
|
});
|
|
|
|
test('it permits destroying variables when token has Variables with write capabilities in its rules', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['destroy'], PathSpec: '*' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canDestroy);
|
|
});
|
|
|
|
test('it handles namespace matching', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'pablo',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['destroy'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
this.ability.path = 'foo/bar';
|
|
this.ability.namespace = 'pablo';
|
|
|
|
assert.ok(this.ability.canDestroy);
|
|
});
|
|
});
|
|
|
|
module('#read', function () {
|
|
test('it does not permit reading variables by default', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.notOk(this.ability.canRead);
|
|
});
|
|
|
|
test('it permits reading variables when token type is management', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'management' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canRead);
|
|
});
|
|
|
|
test('it permits reading variables when acl is disabled', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: false,
|
|
selfToken: { type: 'client' },
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canRead);
|
|
});
|
|
|
|
test('it permits reading variables when token has Variables with read capabilities in its rules', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['read'], PathSpec: '*' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
|
|
assert.ok(this.ability.canRead);
|
|
});
|
|
|
|
test('it handles namespace matching', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['list'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'pablo',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['read'], PathSpec: 'foo/bar' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
this.ability.path = 'foo/bar';
|
|
this.ability.namespace = 'pablo';
|
|
|
|
assert.ok(this.ability.canRead);
|
|
});
|
|
});
|
|
|
|
module('#_nearestMatchingPath', function () {
|
|
test('returns capabilities for an exact path match', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'foo',
|
|
'It should return the exact path match.'
|
|
);
|
|
});
|
|
|
|
test('returns capabilities for the nearest fuzzy match if no exact match', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['write'], PathSpec: 'foo/*' },
|
|
{ Capabilities: ['write'], PathSpec: 'foo/bar/*' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo/bar/baz';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'foo/bar/*',
|
|
'It should return the nearest fuzzy matching path.'
|
|
);
|
|
});
|
|
|
|
test('handles wildcard prefix matches', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: 'foo/*' }],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo/bar/baz';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'foo/*',
|
|
'It should handle wildcard glob.'
|
|
);
|
|
});
|
|
|
|
test('handles wildcard suffix matches', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['write'], PathSpec: '*/bar' },
|
|
{ Capabilities: ['write'], PathSpec: '*/bar/baz' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo/bar/baz';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'*/bar/baz',
|
|
'It should return the nearest ancestor matching path.'
|
|
);
|
|
});
|
|
|
|
test('prioritizes wildcard suffix matches over wildcard prefix matches', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['write'], PathSpec: '*/bar' },
|
|
{ Capabilities: ['write'], PathSpec: 'foo/*' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo/bar/baz';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'foo/*',
|
|
'It should prioritize suffix glob wildcard of prefix glob wildcard.'
|
|
);
|
|
});
|
|
|
|
test('defaults to the glob path if there is no exact match or wildcard matches', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
'Path "*"': {
|
|
Capabilities: ['write'],
|
|
},
|
|
'Path "foo"': {
|
|
Capabilities: ['write'],
|
|
},
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
const path = 'foo/bar/baz';
|
|
|
|
const nearestMatchingPath = this.ability._nearestMatchingPath(path);
|
|
|
|
assert.equal(
|
|
nearestMatchingPath,
|
|
'*',
|
|
'It should default to glob wildcard if no matches.'
|
|
);
|
|
});
|
|
});
|
|
|
|
module('#_computeLengthDiff', function () {
|
|
test('should return the difference in length between a path and a pattern', function (assert) {
|
|
// arrange
|
|
const path = 'foo';
|
|
const pattern = 'bar';
|
|
|
|
// act
|
|
const result = this.ability._computeLengthDiff(pattern, path);
|
|
|
|
// assert
|
|
assert.equal(
|
|
result,
|
|
0,
|
|
'it returns the difference in length between path and pattern'
|
|
);
|
|
});
|
|
|
|
test('should factor the number of globs into consideration', function (assert) {
|
|
// arrange
|
|
const pattern = 'foo*';
|
|
const path = 'bark';
|
|
|
|
// act
|
|
const result = this.ability._computeLengthDiff(pattern, path);
|
|
|
|
// assert
|
|
assert.equal(
|
|
result,
|
|
1,
|
|
'it adds the number of globs in the pattern to the difference'
|
|
);
|
|
});
|
|
});
|
|
|
|
module('#_smallestDifference', function () {
|
|
test('returns the smallest difference in the list', function (assert) {
|
|
// arrange
|
|
const path = 'foo/bar';
|
|
const matchingPath = 'foo/*';
|
|
const matches = ['*/baz', '*', matchingPath];
|
|
|
|
// act
|
|
const result = this.ability._smallestDifference(matches, path);
|
|
|
|
// assert
|
|
assert.equal(
|
|
result,
|
|
matchingPath,
|
|
'It should return the smallest difference path.'
|
|
);
|
|
});
|
|
});
|
|
|
|
module('#allPaths', function () {
|
|
test('it filters by namespace and shows all matching paths on the namespace', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'bar',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['read', 'write'], PathSpec: 'foo' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
this.ability.namespace = 'bar';
|
|
|
|
const allPaths = this.ability.allVariablePathRules;
|
|
|
|
assert.deepEqual(
|
|
allPaths,
|
|
[
|
|
{
|
|
capabilities: ['write'],
|
|
name: 'foo',
|
|
namespace: 'default',
|
|
},
|
|
{
|
|
capabilities: ['read', 'write'],
|
|
name: 'foo',
|
|
namespace: 'bar',
|
|
},
|
|
],
|
|
'It should return the exact path match.'
|
|
);
|
|
});
|
|
|
|
test('it matches if no namespace is selected', function (assert) {
|
|
const mockToken = Service.extend({
|
|
aclEnabled: true,
|
|
selfToken: { type: 'client' },
|
|
selfTokenPolicies: [
|
|
{
|
|
rulesJSON: {
|
|
Namespaces: [
|
|
{
|
|
Name: 'default',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [{ Capabilities: ['write'], PathSpec: 'foo' }],
|
|
},
|
|
},
|
|
{
|
|
Name: 'bar',
|
|
Capabilities: [],
|
|
Variables: {
|
|
Paths: [
|
|
{ Capabilities: ['read', 'write'], PathSpec: 'foo' },
|
|
],
|
|
},
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
});
|
|
|
|
this.owner.register('service:token', mockToken);
|
|
this.ability.namespace = undefined;
|
|
|
|
const allPaths = this.ability.allVariablePathRules;
|
|
|
|
assert.deepEqual(
|
|
allPaths,
|
|
[
|
|
{
|
|
capabilities: ['write'],
|
|
name: 'foo',
|
|
namespace: 'default',
|
|
},
|
|
{
|
|
capabilities: ['read', 'write'],
|
|
name: 'foo',
|
|
namespace: 'bar',
|
|
},
|
|
],
|
|
'It should return both matches separated by namespace.'
|
|
);
|
|
});
|
|
|
|
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'],
|
|
Variables: {
|
|
Paths: [
|
|
{
|
|
Capabilities: ['list'],
|
|
PathSpec: '*',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
Name: 'namespace-1',
|
|
Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
|
|
Variables: {
|
|
Paths: [
|
|
{
|
|
Capabilities: ['list', 'read', 'destroy', 'create'],
|
|
PathSpec: '*',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
{
|
|
Name: 'namespace-2',
|
|
Capabilities: ['list-jobs', 'alloc-exec', 'read-logs'],
|
|
Variables: {
|
|
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.allVariablePathRules;
|
|
|
|
assert.deepEqual(
|
|
allPaths,
|
|
[
|
|
{
|
|
capabilities: ['list'],
|
|
name: '*',
|
|
namespace: '*',
|
|
},
|
|
{
|
|
capabilities: ['list', 'read', 'destroy', 'create'],
|
|
name: '*',
|
|
namespace: 'namespace-1',
|
|
},
|
|
{
|
|
capabilities: ['list', 'read', 'destroy', 'create'],
|
|
name: 'blue/*',
|
|
namespace: 'namespace-2',
|
|
},
|
|
{
|
|
capabilities: ['list', 'read', 'create'],
|
|
name: 'nomad/jobs/*',
|
|
namespace: 'namespace-2',
|
|
},
|
|
],
|
|
'It should return the glob matching namespace match.'
|
|
);
|
|
});
|
|
});
|
|
});
|