Vault Automation 63bbbd163b
UI: Build policy generator (#10985) (#11209)
* wip policy stanza builder

* Implement add and delete new stanza functionality

* refactor to use Set()

* make copy updates, add callback functionality to pass policy to parent

* move policy formatter to util, add test coverage

* =separate acl-policy component into two smaller components, add automation snippets

* reorganize utils, add test coverage

* finish rename

* reduce scope of builder

* fix spacing

* add a ns test, remove unused spacing var

* rename arg

Co-authored-by: claire bontempo <68122737+hellobontempo@users.noreply.github.com>
2025-12-05 22:28:39 +00:00

141 lines
5.5 KiB
JavaScript

/**
* Copyright IBM Corp. 2016, 2025
* SPDX-License-Identifier: BUSL-1.1
*/
import { module, test } from 'qunit';
import { setupTest } from 'ember-qunit';
import {
aclTemplate,
formatCapabilities,
isAclCapability,
ACL_CAPABILITIES,
PolicyStanza,
} from 'core/utils/code-generators/policy';
module('Integration | Util | code-generators/policy', function (hooks) {
setupTest(hooks);
test('aclTemplate: it formats a policy', async function (assert) {
const formatted = aclTemplate('my-path/*', ['list', 'read', 'delete']);
const expected = `path "my-path/*" {
capabilities = ["read", "delete", "list"]
}`;
assert.strictEqual(formatted, expected, 'it formats an ACL policy');
});
test('aclTemplate: it handles empty path and capabilities', async function (assert) {
const formatted = aclTemplate('', []);
const expected = `path "" {
capabilities = []
}`;
assert.strictEqual(formatted, expected, 'it formats empty policy');
});
test('aclTemplate: it handles single capability', async function (assert) {
const formatted = aclTemplate('auth/token/lookup-self', ['read']);
const expected = `path "auth/token/lookup-self" {
capabilities = ["read"]
}`;
assert.strictEqual(formatted, expected, 'it formats policy with single capability');
});
test('formatCapabilities: it formats capabilities in consistent order', async function (assert) {
const formatted = formatCapabilities(['list', 'read', 'delete']);
const expected = '"read", "delete", "list"';
assert.strictEqual(formatted, expected, 'it formats capabilities in ACL_CAPABILITIES order');
});
test('formatCapabilities: it filters out invalid capabilities', async function (assert) {
const formatted = formatCapabilities(['read', 'invalid', 'list']);
const expected = '"read", "list"';
assert.strictEqual(formatted, expected, 'it filters out invalid capabilities');
});
test('formatCapabilities: it returns empty string for empty array', async function (assert) {
const formatted = formatCapabilities([]);
assert.strictEqual(formatted, '', 'it returns empty string for no capabilities');
});
test('formatCapabilities: it handles single capability', async function (assert) {
const formatted = formatCapabilities(['read']);
const expected = '"read"';
assert.strictEqual(formatted, expected, 'it formats single capability');
});
test('formatCapabilities: it handles all capabilities', async function (assert) {
const sorted = [...ACL_CAPABILITIES].sort(); // alphabetize so input order is different than expected output
const formatted = formatCapabilities(sorted);
const expected = '"create", "read", "update", "delete", "list", "patch", "sudo"';
assert.strictEqual(formatted, expected, 'it formats all capabilities in order');
});
test('isAclCapability: it returns true for valid capabilities', async function (assert) {
ACL_CAPABILITIES.forEach((cap) => {
assert.true(isAclCapability(cap), `${cap} is a valid capability`);
});
});
test('isAclCapability: it returns false for invalid capabilities', async function (assert) {
assert.false(isAclCapability('invalid'), 'invalid is not a valid capability');
assert.false(isAclCapability('write'), 'write is not a valid capability');
assert.false(isAclCapability(''), 'empty string is not a valid capability');
assert.false(isAclCapability('READ'), 'uppercase READ is not a valid capability');
});
test('PolicyStanza: it initializes with empty capabilities and path', async function (assert) {
const stanza = new PolicyStanza();
assert.strictEqual(stanza.path, '', 'path is empty');
assert.strictEqual(stanza.capabilities.size, 0, 'capabilities set is empty');
});
test('PolicyStanza: it generates preview for single capability', async function (assert) {
const stanza = new PolicyStanza();
stanza.path = 'secret/data/*';
stanza.capabilities.add('read');
const expected = `path "secret/data/*" {
capabilities = ["read"]
}`;
assert.strictEqual(stanza.preview, expected, 'it generates correct preview');
});
test('PolicyStanza: it generates preview for multiple capabilities', async function (assert) {
const stanza = new PolicyStanza();
stanza.path = 'auth/*';
stanza.capabilities.add('list');
stanza.capabilities.add('read');
stanza.capabilities.add('create');
const expected = `path "auth/*" {
capabilities = ["create", "read", "list"]
}`;
assert.strictEqual(stanza.preview, expected, 'it generates preview with multiple capabilities');
});
test('PolicyStanza: it generates preview without path and capabilities', async function (assert) {
const stanza = new PolicyStanza();
const expected = `path "" {
capabilities = []
}`;
assert.strictEqual(stanza.preview, expected, 'it generates preview with empty capabilities');
});
test('PolicyStanza: it updates preview when capabilities change', async function (assert) {
const stanza = new PolicyStanza();
stanza.path = 'secret/*';
stanza.capabilities.add('read');
const firstPreview = stanza.preview;
stanza.capabilities.add('list');
const secondPreview = stanza.preview;
const expected = `path "secret/*" {
capabilities = ["read", "list"]
}`;
assert.notStrictEqual(firstPreview, secondPreview, 'preview updates when capabilities change');
assert.true(secondPreview.includes('"read", "list"'), 'new preview includes both capabilities');
assert.strictEqual(secondPreview, expected, 'new preview reflects updates');
});
});