diff --git a/addons/api/addon/models/credential.js b/addons/api/addon/models/credential.js index eb5474206..a280ae071 100644 --- a/addons/api/addon/models/credential.js +++ b/addons/api/addon/models/credential.js @@ -18,6 +18,14 @@ export default class CredentialModel extends GeneratedCredentialModel { return this.id.startsWith('cred'); } + /** + * True if type is `json`. + * @type {boolean} + */ + get isJSON() { + return this.type === 'json'; + } + /** * True if credential is an unknown type. * @type {boolean} diff --git a/addons/api/addon/serializers/credential.js b/addons/api/addon/serializers/credential.js index 5c61d93dc..78878b38c 100644 --- a/addons/api/addon/serializers/credential.js +++ b/addons/api/addon/serializers/credential.js @@ -20,7 +20,9 @@ export default class CredentialSerializer extends ApplicationSerializer { delete serialized.attributes.username; if (serialized?.attributes?.json_object) { - serialized.attributes.object = JSON.parse(serialized.attributes.json_object); + serialized.attributes.object = JSON.parse( + serialized.attributes.json_object + ); delete serialized.attributes.json_object; } } diff --git a/addons/api/tests/unit/models/credential-test.js b/addons/api/tests/unit/models/credential-test.js index 21b5b98ce..a0f0497e2 100644 --- a/addons/api/tests/unit/models/credential-test.js +++ b/addons/api/tests/unit/models/credential-test.js @@ -22,6 +22,22 @@ module('Unit | Model | credential', function (hooks) { assert.false(modelC.isStatic); }); + test('it has isJSON property and returns the expected values', async function (assert) { + assert.expect(3); + const store = this.owner.lookup('service:store'); + const modelA = store.createRecord('credential', { type: 'json' }); + const modelB = store.createRecord('credential', { + type: 'username_password', + }); + const modelC = store.createRecord('credential', { + type: 'ssh_private_key', + }); + + assert.true(modelA.isJSON); + assert.false(modelB.isJSON); + assert.false(modelC.isJSON); + }); + test('it has isUnknown property and returns the expected values', async function (assert) { assert.expect(3); const store = this.owner.lookup('service:store'); diff --git a/ui/admin/app/abilities/credential.js b/ui/admin/app/abilities/credential.js index 70bd961cd..825341313 100644 --- a/ui/admin/app/abilities/credential.js +++ b/ui/admin/app/abilities/credential.js @@ -5,9 +5,13 @@ export default class OverrideCredentialAbility extends CredentialAbility { // =service @service features; + /** + * This override ensures that JSON credentials may be read only if the + * json-credentials feature flag is enabled. All other types are subject + * to the standard logic found in the api addon. + */ get canRead() { - return this.features.isEnabled('json-credentials') || - this.model.type !== 'json' + return this.features.isEnabled('json-credentials') || !this.model.isJSON ? super.canRead : false; } diff --git a/ui/admin/app/components/form/field/json-secret/index.js b/ui/admin/app/components/form/field/json-secret/index.js index d70539cdb..d1e26ec09 100644 --- a/ui/admin/app/components/form/field/json-secret/index.js +++ b/ui/admin/app/components/form/field/json-secret/index.js @@ -41,4 +41,4 @@ export default class FormFieldJsonSecretComponent extends Component { enableEditing() { if (!this.args.disabled) this.editing = true; } -} \ No newline at end of file +} diff --git a/ui/admin/app/components/form/worker/create-worker-led/index.js b/ui/admin/app/components/form/worker/create-worker-led/index.js index 3bc1a37bf..24afe932f 100644 --- a/ui/admin/app/components/form/worker/create-worker-led/index.js +++ b/ui/admin/app/components/form/worker/create-worker-led/index.js @@ -108,18 +108,19 @@ ${listenerText} * @type {string} */ get installBoundaryText() { - if (this.features.isEnabled('byow-pki-hcp-cluster-id')) { - return `wget -q https://releases.hashicorp.com/boundary-worker/0.11.2+hcp/boundary-worker_0.11.2+hcp_linux_amd64.zip ;\\ -sudo apt-get update && sudo apt-get install unzip ;\\ -unzip *.zip ;\\ -./boundary-worker server -config="${ - this.configFilePath || '' - }/pki-worker.hcl"`; - } - return `curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - ;\\ + const configPath = this.configFilePath || ''; + const ossContent = `curl -fsSL https://apt.releases.hashicorp.com/gpg | sudo apt-key add - ;\\ sudo apt-add-repository "deb [arch=amd64] https://apt.releases.hashicorp.com $(lsb_release -cs) main" ;\\ sudo apt-get update && sudo apt-get install boundary ;\\ -boundary server -config="${this.configFilePath || ''}/pki-worker.hcl"`; +boundary server -config="${configPath}/pki-worker.hcl"`; + const hcpContent = `wget -q https://releases.hashicorp.com/boundary-worker/0.11.2+hcp/boundary-worker_0.11.2+hcp_linux_amd64.zip ;\\ +sudo apt-get update && sudo apt-get install unzip ;\\ +unzip *.zip ;\\ +./boundary-worker server -config="${configPath}/pki-worker.hcl"`; + + return this.features.isEnabled('byow-pki-hcp-cluster-id') + ? hcpContent + : ossContent; } /** diff --git a/ui/admin/tests/acceptance/credential-store/credentials/delete-test.js b/ui/admin/tests/acceptance/credential-store/credentials/delete-test.js index c5474d917..108cc7db1 100644 --- a/ui/admin/tests/acceptance/credential-store/credentials/delete-test.js +++ b/ui/admin/tests/acceptance/credential-store/credentials/delete-test.js @@ -80,8 +80,7 @@ module( .length; }; getJSONCredentialCount = () => { - return this.server.schema.credentials.where({ type: 'json' }) - .length; + return this.server.schema.credentials.where({ type: 'json' }).length; }; authenticateSession({}); }); @@ -114,15 +113,11 @@ module( test('can delete JSON credential', async function (assert) { assert.expect(2); - const jsonCredentialCount = - getJSONCredentialCount(); + const jsonCredentialCount = getJSONCredentialCount(); await visit(urls.jsonCredential); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); assert.strictEqual(currentURL(), urls.credentials); - assert.strictEqual( - getJSONCredentialCount(), - jsonCredentialCount - 1 - ); + assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount - 1); }); test('cannot delete a username & password credential without proper authorization', async function (assert) { @@ -165,8 +160,7 @@ module( test('cannot delete a JSON credential without proper authorization', async function (assert) { assert.expect(3); - const jsonCredentialCount = - getJSONCredentialCount(); + const jsonCredentialCount = getJSONCredentialCount(); instances.jsonCredential.authorized_actions = instances.jsonCredential.authorized_actions.filter( (item) => item !== 'delete' @@ -176,10 +170,7 @@ module( assert .dom('.rose-layout-page-actions .rose-dropdown-button-danger') .doesNotExist(); - assert.strictEqual( - getJSONCredentialCount(), - jsonCredentialCount - ); + assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount); }); test('can accept delete username & password credential via dialog', async function (assert) { @@ -218,16 +209,12 @@ module( assert.expect(2); const confirmService = this.owner.lookup('service:confirm'); confirmService.enabled = true; - const jsonCredentialCount = - getJSONCredentialCount(); + const jsonCredentialCount = getJSONCredentialCount(); await visit(urls.jsonCredential); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); await click('.rose-dialog footer .rose-button-primary'); assert.strictEqual(currentURL(), urls.credentials); - assert.strictEqual( - getJSONCredentialCount(), - jsonCredentialCount - 1 - ); + assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount - 1); }); test('can cancel delete username & password credential via dialog', async function (assert) { @@ -266,16 +253,12 @@ module( assert.expect(2); const confirmService = this.owner.lookup('service:confirm'); confirmService.enabled = true; - const jsonCredentialCount = - getJSONCredentialCount(); + const jsonCredentialCount = getJSONCredentialCount(); await visit(urls.jsonCredential); await click('.rose-layout-page-actions .rose-dropdown-button-danger'); await click('.rose-dialog footer .rose-button-secondary'); assert.strictEqual(currentURL(), urls.jsonCredential); - assert.strictEqual( - getJSONCredentialCount(), - jsonCredentialCount - ); + assert.strictEqual(getJSONCredentialCount(), jsonCredentialCount); }); test('deleting a username & password credential which errors displays error message', async function (assert) { diff --git a/ui/admin/tests/integration/components/form/field/json-secret-skeleton-test.js b/ui/admin/tests/integration/components/form/field/json-secret-skeleton-test.js index 8b2db71f5..7853fbf62 100644 --- a/ui/admin/tests/integration/components/form/field/json-secret-skeleton-test.js +++ b/ui/admin/tests/integration/components/form/field/json-secret-skeleton-test.js @@ -3,18 +3,21 @@ import { setupRenderingTest } from 'ember-qunit'; import { render } from '@ember/test-helpers'; import { hbs } from 'ember-cli-htmlbars'; -module('Integration | Component | form/field/json-secret/skeleton', function (hooks) { - setupRenderingTest(hooks); +module( + 'Integration | Component | form/field/json-secret/skeleton', + function (hooks) { + setupRenderingTest(hooks); - test('it renders', async function (assert) { - assert.expect(2); - await render(hbs` + test('it renders', async function (assert) { + assert.expect(2); + await render(hbs` Skeleton Message `); - assert.dom('.secret-editor-skeleton-wrapper').isVisible(); - assert.dom('.secret-editor-skeleton-message').hasText('Skeleton Message'); - }); -}); \ No newline at end of file + assert.dom('.secret-editor-skeleton-wrapper').isVisible(); + assert.dom('.secret-editor-skeleton-message').hasText('Skeleton Message'); + }); + } +); diff --git a/ui/admin/tests/integration/components/form/field/json-secret-test.js b/ui/admin/tests/integration/components/form/field/json-secret-test.js index d7f80cdb9..2b1112df0 100644 --- a/ui/admin/tests/integration/components/form/field/json-secret-test.js +++ b/ui/admin/tests/integration/components/form/field/json-secret-test.js @@ -26,7 +26,9 @@ module('Integration | Component | form/field/json-secret', function (hooks) { assert.dom('.secret-editor').isVisible(); assert.dom('.secret-editor-json').isVisible(); - assert.dom('.secret-editor-json .CodeMirror-line').hasText(this.model.json_object); + assert + .dom('.secret-editor-json .CodeMirror-line') + .hasText(this.model.json_object); }); test('it renders the disabled editor', async function (assert) { @@ -42,7 +44,9 @@ module('Integration | Component | form/field/json-secret', function (hooks) { assert.dom('.secret-editor').isVisible(); assert.dom('.secret-editor-json').doesNotExist(); - assert.dom('.secret-editor-skeleton-wrapper').hasText('This secret is saved but won\'t be displayed'); + assert + .dom('.secret-editor-skeleton-wrapper') + .hasText("This secret is saved but won't be displayed"); }); test('it renders the actionable editor', async function (assert) { @@ -58,7 +62,9 @@ module('Integration | Component | form/field/json-secret', function (hooks) { assert.dom('.secret-editor').isVisible(); assert.dom('.secret-editor-json').doesNotExist(); - assert.dom('.secret-editor-skeleton-wrapper').hasText('Click to replace this secret'); + assert + .dom('.secret-editor-skeleton-wrapper') + .hasText('Click to replace this secret'); }); test('it renders the active editor from the actionable view', async function (assert) { @@ -74,9 +80,11 @@ module('Integration | Component | form/field/json-secret', function (hooks) { assert.dom('.secret-editor').isVisible(); assert.dom('.secret-editor-json').doesNotExist(); - assert.dom('.secret-editor-skeleton-wrapper').hasText('Click to replace this secret'); + assert + .dom('.secret-editor-skeleton-wrapper') + .hasText('Click to replace this secret'); - await click('button') + await click('button'); assert.dom('.secret-editor-json').isVisible(); }); -}); \ No newline at end of file +}); diff --git a/ui/admin/tests/unit/abilities/credential-test.js b/ui/admin/tests/unit/abilities/credential-test.js index 4a0e3ef78..a8a719f7e 100644 --- a/ui/admin/tests/unit/abilities/credential-test.js +++ b/ui/admin/tests/unit/abilities/credential-test.js @@ -4,63 +4,74 @@ import { setupTest } from 'ember-qunit'; module('Unit | Abilities | credential', function (hooks) { setupTest(hooks); - test('canRead', function (assert) { + let features; + + hooks.beforeEach(function () { + // Enable json-credentials feature by default + features = this.owner.lookup('service:features'); + features.enable('json-credentials'); + }); + + test('can read credentials, including JSON credentials, when authorized and json-credentials feature is enabled', function (assert) { assert.expect(3); const service = this.owner.lookup('service:can'); - const credential = { + const store = this.owner.lookup('service:store'); + const credential = store.createRecord('credential', { authorized_actions: ['read'], type: 'json', - }; - assert.ok(service.can('read credential', credential)); + }); + assert.true(service.can('read credential', credential)); credential.type = 'username_password'; - assert.ok(service.can('read credential', credential)); + assert.true(service.can('read credential', credential)); credential.type = 'ssh_private_key'; - assert.ok(service.can('read credential', credential)); + assert.true(service.can('read credential', credential)); }); - test('cannot read without proper authorized actions', function (assert) { + test('cannot read credentials, including JSON credentials, when unauthorized and json-credentials feature is enabled', function (assert) { assert.expect(3); const service = this.owner.lookup('service:can'); - const credential = { + const store = this.owner.lookup('service:store'); + const credential = store.createRecord('credential', { authorized_actions: [], type: 'json', - }; - assert.notOk(service.can('read credential', credential)); + }); + assert.false(service.can('read credential', credential)); credential.type = 'username_password'; - assert.notOk(service.can('read credential', credential)); + assert.false(service.can('read credential', credential)); credential.type = 'ssh_private_key'; - assert.notOk(service.can('read credential', credential)); + assert.false(service.can('read credential', credential)); }); - test('cannot read without proper authorized actions and json-credentials disabled', function (assert) { + test('cannot read credentials, including JSON credentials, when unauthorized and json-credentials feature is disabled', function (assert) { assert.expect(3); const service = this.owner.lookup('service:can'); const featuresService = this.owner.lookup('service:features'); featuresService.disable('json-credentials'); - const credential = { + const store = this.owner.lookup('service:store'); + const credential = store.createRecord('credential', { authorized_actions: [], type: 'json', - }; - assert.notOk(service.can('read credential', credential)); + }); + assert.false(service.can('read credential', credential)); credential.type = 'username_password'; - assert.notOk(service.can('read credential', credential)); + assert.false(service.can('read credential', credential)); credential.type = 'ssh_private_key'; - assert.notOk(service.can('read credential', credential)); + assert.false(service.can('read credential', credential)); }); - test('cannot read when json-credentials is disabled', function (assert) { + test('can read credentials, excepting JSON credentials, when authorized and json-credentials feature is disabled', function (assert) { assert.expect(3); + features.disable('json-credentials'); const service = this.owner.lookup('service:can'); - const featuresService = this.owner.lookup('service:features'); - featuresService.disable('json-credentials'); - const credential = { + const store = this.owner.lookup('service:store'); + const credential = store.createRecord('credential', { authorized_actions: ['read'], type: 'json', - }; - assert.notOk(service.can('read credential', credential)); + }); + assert.false(service.can('read credential', credential)); credential.type = 'username_password'; - assert.ok(service.can('read credential', credential)); + assert.true(service.can('read credential', credential)); credential.type = 'ssh_private_key'; - assert.ok(service.can('read credential', credential)); + assert.true(service.can('read credential', credential)); }); });