mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-10-30 02:02:43 +00:00
Add directory paths to KV capabilities checks (#24404)
* add getter to metadata model * add changelog and data model fix * add test coverage * add nested create coverage * Update 24404.txt * remove from data model * return to how it was
This commit is contained in:
3
changelog/24404.txt
Normal file
3
changelog/24404.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
```release-note:bug
|
||||||
|
ui: fix issue where kv v2 capabilities checks were not passing in the full secret path if secret was inside a directory.
|
||||||
|
```
|
||||||
@@ -95,9 +95,14 @@ export default class KvSecretMetadataModel extends Model {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get permissionsPath() {
|
||||||
|
return this.fullSecretPath || this.path;
|
||||||
|
}
|
||||||
|
|
||||||
// permissions needed for the list view where kv/data has not yet been called. Allows us to conditionally show action items in the LinkedBlock popups.
|
// permissions needed for the list view where kv/data has not yet been called. Allows us to conditionally show action items in the LinkedBlock popups.
|
||||||
@lazyCapabilities(apiPath`${'backend'}/data/${'path'}`, 'backend', 'path') dataPath;
|
@lazyCapabilities(apiPath`${'backend'}/data/${'permissionsPath'}`, 'backend', 'permissionsPath') dataPath;
|
||||||
@lazyCapabilities(apiPath`${'backend'}/metadata/${'path'}`, 'backend', 'path') metadataPath;
|
@lazyCapabilities(apiPath`${'backend'}/metadata/${'permissionsPath'}`, 'backend', 'permissionsPath')
|
||||||
|
metadataPath;
|
||||||
|
|
||||||
get canDeleteMetadata() {
|
get canDeleteMetadata() {
|
||||||
return this.metadataPath.get('canDelete') !== false;
|
return this.metadataPath.get('canDelete') !== false;
|
||||||
|
|||||||
@@ -95,7 +95,11 @@
|
|||||||
{{/if}}
|
{{/if}}
|
||||||
{{#if metadata.canCreateVersionData}}
|
{{#if metadata.canCreateVersionData}}
|
||||||
<li>
|
<li>
|
||||||
<LinkTo @route="secret.details.edit" @model={{metadata.fullSecretPath}}>
|
<LinkTo
|
||||||
|
@route="secret.details.edit"
|
||||||
|
@model={{metadata.fullSecretPath}}
|
||||||
|
data-test-popup-create-new-version
|
||||||
|
>
|
||||||
Create new version
|
Create new version
|
||||||
</LinkTo>
|
</LinkTo>
|
||||||
</li>
|
</li>
|
||||||
@@ -106,6 +110,7 @@
|
|||||||
@isInDropdown={{true}}
|
@isInDropdown={{true}}
|
||||||
@onConfirmAction={{fn this.onDelete metadata}}
|
@onConfirmAction={{fn this.onDelete metadata}}
|
||||||
@confirmMessage="This will permanently delete this secret and all its versions."
|
@confirmMessage="This will permanently delete this secret and all its versions."
|
||||||
|
data-test-popup-metadata-delete
|
||||||
/>
|
/>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
{{/if}}
|
{{/if}}
|
||||||
|
|||||||
@@ -1003,6 +1003,26 @@ module('Acceptance | kv-v2 workflow | secret and version create', function (hook
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module('secret-nested-creator persona', function (hooks) {
|
||||||
|
hooks.beforeEach(async function () {
|
||||||
|
const token = await runCmd(
|
||||||
|
tokenWithPolicyCmd('secret-nested-creator', personas.secretNestedCreator(this.backend))
|
||||||
|
);
|
||||||
|
await authPage.login(token);
|
||||||
|
clearRecords(this.store);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
test('can create a secret from the nested list view (snc)', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
// go to nested secret directory list view
|
||||||
|
await visit(`/vault/secrets/${this.backend}/kv/list/app/`);
|
||||||
|
// correct popup menu items appear on list view
|
||||||
|
const popupSelector = `${PAGE.list.item('first')} ${PAGE.popup}`;
|
||||||
|
await click(popupSelector);
|
||||||
|
assert.dom(PAGE.list.listMenuCreate).exists('shows the option to create new version');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module('enterprise controlled access persona', function (hooks) {
|
module('enterprise controlled access persona', function (hooks) {
|
||||||
hooks.beforeEach(async function () {
|
hooks.beforeEach(async function () {
|
||||||
this.controlGroup = this.owner.lookup('service:control-group');
|
this.controlGroup = this.owner.lookup('service:control-group');
|
||||||
|
|||||||
@@ -40,9 +40,11 @@ module('Acceptance | kv-v2 workflow | delete, undelete, destroy', function (hook
|
|||||||
this.store = this.owner.lookup('service:store');
|
this.store = this.owner.lookup('service:store');
|
||||||
this.backend = `kv-delete-${uuidv4()}`;
|
this.backend = `kv-delete-${uuidv4()}`;
|
||||||
this.secretPath = 'bad-secret';
|
this.secretPath = 'bad-secret';
|
||||||
|
this.nestedSecretPath = 'app/nested/bad-secret';
|
||||||
await authPage.login();
|
await authPage.login();
|
||||||
await runCmd(mountEngineCmd('kv-v2', this.backend), false);
|
await runCmd(mountEngineCmd('kv-v2', this.backend), false);
|
||||||
await writeVersionedSecret(this.backend, this.secretPath, 'foo', 'bar', 4);
|
await writeVersionedSecret(this.backend, this.secretPath, 'foo', 'bar', 4);
|
||||||
|
await writeVersionedSecret(this.backend, this.nestedSecretPath, 'foo', 'bar', 1);
|
||||||
await writeVersionedSecret(this.backend, 'nuke', 'foo', 'bar', 2);
|
await writeVersionedSecret(this.backend, 'nuke', 'foo', 'bar', 2);
|
||||||
// Delete latest version for testing undelete for users that can't delete
|
// Delete latest version for testing undelete for users that can't delete
|
||||||
await runCmd(deleteLatestCmd(this.backend, 'nuke'));
|
await runCmd(deleteLatestCmd(this.backend, 'nuke'));
|
||||||
@@ -353,6 +355,33 @@ module('Acceptance | kv-v2 workflow | delete, undelete, destroy', function (hook
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
module('secret-nested-creator persona', function (hooks) {
|
||||||
|
hooks.beforeEach(async function () {
|
||||||
|
const token = await runCmd(
|
||||||
|
tokenWithPolicyCmd('secret-nested-creator', personas.secretNestedCreator(this.backend))
|
||||||
|
);
|
||||||
|
await authPage.login(token);
|
||||||
|
clearRecords(this.store);
|
||||||
|
return;
|
||||||
|
});
|
||||||
|
test('can delete all secret versions from the nested list view (snc)', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
// go to nested secret directory list view
|
||||||
|
await visit(`/vault/secrets/${this.backend}/kv/list/app/nested`);
|
||||||
|
// correct popup menu items appear on list view
|
||||||
|
const popupSelector = `${PAGE.list.item('bad-secret')} ${PAGE.popup}`;
|
||||||
|
await click(popupSelector);
|
||||||
|
assert.dom(PAGE.list.listMenuDelete).exists('shows the option to permanently delete');
|
||||||
|
});
|
||||||
|
test('can not delete all secret versions from root list view (snc)', async function (assert) {
|
||||||
|
assert.expect(1);
|
||||||
|
// go to root secret directory list view
|
||||||
|
await visit(`/vault/secrets/${this.backend}/kv/list`);
|
||||||
|
// shows overview card and not list view
|
||||||
|
assert.dom(PAGE.list.overviewCard).exists('renders overview card');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
module('secret-creator persona', function (hooks) {
|
module('secret-creator persona', function (hooks) {
|
||||||
hooks.beforeEach(async function () {
|
hooks.beforeEach(async function () {
|
||||||
const token = await runCmd(tokenWithPolicyCmd('secret-creator', personas.secretCreator(this.backend)));
|
const token = await runCmd(tokenWithPolicyCmd('secret-creator', personas.secretCreator(this.backend)));
|
||||||
|
|||||||
@@ -58,6 +58,8 @@ export const PAGE = {
|
|||||||
createSecret: '[data-test-toolbar-create-secret]',
|
createSecret: '[data-test-toolbar-create-secret]',
|
||||||
item: (secret) => (!secret ? '[data-test-list-item]' : `[data-test-list-item="${secret}"]`),
|
item: (secret) => (!secret ? '[data-test-list-item]' : `[data-test-list-item="${secret}"]`),
|
||||||
filter: `[data-test-kv-list-filter]`,
|
filter: `[data-test-kv-list-filter]`,
|
||||||
|
listMenuDelete: `[data-test-popup-metadata-delete]`,
|
||||||
|
listMenuCreate: `[data-test-popup-create-new-version]`,
|
||||||
overviewCard: '[data-test-overview-card-container="View secret"]',
|
overviewCard: '[data-test-overview-card-container="View secret"]',
|
||||||
overviewInput: '[data-test-view-secret] input',
|
overviewInput: '[data-test-view-secret] input',
|
||||||
overviewButton: '[data-test-get-secret-detail]',
|
overviewButton: '[data-test-get-secret-detail]',
|
||||||
|
|||||||
@@ -25,6 +25,14 @@ export const dataPolicy = ({ backend, secretPath = '*', capabilities = root }) =
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const dataNestedPolicy = ({ backend, secretPath = '*', capabilities = root }) => {
|
||||||
|
return `
|
||||||
|
path "${backend}/data/app/${secretPath}" {
|
||||||
|
capabilities = [${format(capabilities)}]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
export const metadataPolicy = ({ backend, secretPath = '*', capabilities = root }) => {
|
export const metadataPolicy = ({ backend, secretPath = '*', capabilities = root }) => {
|
||||||
// "delete" capability on this path can destroy all versions
|
// "delete" capability on this path can destroy all versions
|
||||||
return `
|
return `
|
||||||
@@ -34,6 +42,14 @@ export const metadataPolicy = ({ backend, secretPath = '*', capabilities = root
|
|||||||
`;
|
`;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const metadataNestedPolicy = ({ backend, secretPath = '*', capabilities = root }) => {
|
||||||
|
return `
|
||||||
|
path "${backend}/metadata/app/${secretPath}" {
|
||||||
|
capabilities = [${format(capabilities)}]
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
};
|
||||||
|
|
||||||
export const metadataListPolicy = (backend) => {
|
export const metadataListPolicy = (backend) => {
|
||||||
return `
|
return `
|
||||||
path "${backend}/metadata" {
|
path "${backend}/metadata" {
|
||||||
@@ -71,11 +87,13 @@ export const personas = {
|
|||||||
dataListReader: (backend) =>
|
dataListReader: (backend) =>
|
||||||
dataPolicy({ backend, capabilities: ['read', 'delete'] }) + metadataListPolicy(backend),
|
dataPolicy({ backend, capabilities: ['read', 'delete'] }) + metadataListPolicy(backend),
|
||||||
metadataMaintainer: (backend) =>
|
metadataMaintainer: (backend) =>
|
||||||
metadataListPolicy(backend) +
|
|
||||||
metadataPolicy({ backend, capabilities: ['create', 'read', 'update', 'list'] }) +
|
metadataPolicy({ backend, capabilities: ['create', 'read', 'update', 'list'] }) +
|
||||||
deleteVersionsPolicy({ backend }) +
|
deleteVersionsPolicy({ backend }) +
|
||||||
undeleteVersionsPolicy({ backend }) +
|
undeleteVersionsPolicy({ backend }) +
|
||||||
destroyVersionsPolicy({ backend }),
|
destroyVersionsPolicy({ backend }),
|
||||||
|
secretNestedCreator: (backend) =>
|
||||||
|
dataNestedPolicy({ backend, capabilities: ['create', 'update'] }) +
|
||||||
|
metadataNestedPolicy({ backend, capabilities: ['list', 'delete'] }),
|
||||||
secretCreator: (backend) =>
|
secretCreator: (backend) =>
|
||||||
dataPolicy({ backend, capabilities: ['create', 'update'] }) +
|
dataPolicy({ backend, capabilities: ['create', 'update'] }) +
|
||||||
metadataPolicy({ backend, capabilities: ['delete'] }),
|
metadataPolicy({ backend, capabilities: ['delete'] }),
|
||||||
|
|||||||
Reference in New Issue
Block a user