diff --git a/addons/api/addon/generated/models/credential-store.js b/addons/api/addon/generated/models/credential-store.js index 47b39fbcd..e92bcfee7 100644 --- a/addons/api/addon/generated/models/credential-store.js +++ b/addons/api/addon/generated/models/credential-store.js @@ -82,6 +82,7 @@ export default class GeneratedCredentialStoreModel extends BaseModel { @attr('string', { for: 'vault', isNestedAttribute: true, + isSecret: true, description: 'A token used for accessing Vault.', }) token; diff --git a/addons/api/addon/generated/models/credential.js b/addons/api/addon/generated/models/credential.js index e9a5ad386..fb51ad5e9 100644 --- a/addons/api/addon/generated/models/credential.js +++ b/addons/api/addon/generated/models/credential.js @@ -58,6 +58,7 @@ export default class GeneratedCredentialModel extends BaseModel { @attr('string', { for: 'username_password', isNestedAttribute: true, + isSecret: true, description: 'The password for credential.', }) password; @@ -66,6 +67,7 @@ export default class GeneratedCredentialModel extends BaseModel { @attr('string', { for: 'ssh_private_key', isNestedAttribute: true, + isSecret: true, description: 'The private key for credential.', }) private_key; @@ -73,6 +75,7 @@ export default class GeneratedCredentialModel extends BaseModel { @attr('string', { for: 'ssh_private_key', isNestedAttribute: true, + isSecret: true, description: 'The private key passphrase for credential.', }) private_key_passphrase; diff --git a/addons/api/addon/serializers/application.js b/addons/api/addon/serializers/application.js index 982d8e4f9..f1e582cbb 100644 --- a/addons/api/addon/serializers/application.js +++ b/addons/api/addon/serializers/application.js @@ -39,6 +39,10 @@ export default class ApplicationSerializer extends RESTSerializer { serializeAttribute(snapshot, json, key, attribute) { const { type, options } = attribute; let value = super.serializeAttribute(...arguments); + // Remove secret attributes that are null or empty + if (options.isSecret) { + if (type === 'string' && !json[key]) delete json[key]; + } // Convert empty strings to null. if (type === 'string' && json[key] === '') json[key] = null; // Do not serialize read-only attributes. diff --git a/addons/api/addon/serializers/credential-store.js b/addons/api/addon/serializers/credential-store.js index e06137dfa..f715c7f97 100644 --- a/addons/api/addon/serializers/credential-store.js +++ b/addons/api/addon/serializers/credential-store.js @@ -20,8 +20,6 @@ export default class CredentialStoreSerializer extends ApplicationSerializer { serializeVault() { const serialized = super.serialize(...arguments); if (serialized.attributes) { - // Token cannot be unset. If it's falsy, it must be removed. - if (!serialized.attributes?.token) delete serialized.attributes.token; // Client certificate key cannot be unset when certificate is set. // If it's falsy, it must be removed. if ( diff --git a/addons/api/addon/serializers/credential.js b/addons/api/addon/serializers/credential.js index afe6f2274..d2e3eed20 100644 --- a/addons/api/addon/serializers/credential.js +++ b/addons/api/addon/serializers/credential.js @@ -13,46 +13,6 @@ export default class CredentialSerializer extends ApplicationSerializer { */ serializeScopeID = false; - serialize(snapshot) { - switch (snapshot.record.type) { - case 'username_password': - return this.serializeUsernamePassword(...arguments); - case 'ssh_private_key': - default: - return this.serializeSSHPrivateKey(...arguments); - } - } - - serializeUsernamePassword() { - const serialized = super.serialize(...arguments); - // Remove non-username_password type attributes - delete serialized.attributes.private_key; - delete serialized.attributes.private_key_passphrase; - // Remove password from the payload if null - // or empty string. This allows the user to only update - // the username without providing the password again. - // Users cannot unset the password, they would have - // to delete the credential entirely. - if (!serialized?.attributes?.password) - delete serialized.attributes.password; - return serialized; - } - - serializeSSHPrivateKey() { - const serialized = super.serialize(...arguments); - // Remove non-ssh_private_key type attributes - delete serialized.attributes.password; - // Remove private_key and/or private_key_passphrase from the payload - // if null or empty string. This allows the user to only update - // the username without providing the private key and passphrase again. - // Users cannot unset the private key or passphrase, they would have - // to delete the credential entirely. - if (!serialized?.attributes?.private_key_passphrase) - delete serialized.attributes.private_key_passphrase; - if (!serialized?.attributes?.private_key) - delete serialized.attributes.private_key; - return serialized; - } normalize(typeClass, hash, ...rest) { const normalizedHash = copy(hash, true); diff --git a/addons/api/tests/unit/serializers/application-test.js b/addons/api/tests/unit/serializers/application-test.js index 545a8377c..793606178 100644 --- a/addons/api/tests/unit/serializers/application-test.js +++ b/addons/api/tests/unit/serializers/application-test.js @@ -107,6 +107,33 @@ module('Unit | Serializer | application', function (hooks) { }); }); + test('it serializes secret attributes correctly', function (assert) { + assert.expect(1); + const store = this.owner.lookup('service:store'); + const record = store.createRecord('credential', { + private_key_passphrase: undefined, + private_key: null, + password: '', + username: 'user', + credential_store_id: 'csst_i7p1eu0Nw8', + type: 'username_password', + name: 'Name', + description: 'Description', + version: 1, + }); + const serializedRecord = record.serialize(); + assert.deepEqual(serializedRecord, { + attributes: { + username: 'user', + }, + credential_store_id: 'csst_i7p1eu0Nw8', + type: 'username_password', + name: 'Name', + description: 'Description', + version: 1, + }); + }); + test('it normalizes array records from an `items` root key', function (assert) { assert.expect(1); const store = this.owner.lookup('service:store'); diff --git a/addons/api/tests/unit/serializers/credential-test.js b/addons/api/tests/unit/serializers/credential-test.js index 566ea6e13..af930ff35 100644 --- a/addons/api/tests/unit/serializers/credential-test.js +++ b/addons/api/tests/unit/serializers/credential-test.js @@ -97,7 +97,7 @@ module('Unit | Serializer | credential', function (hooks) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('credential'); const record = store.createRecord('credential', { - private_key_passphrase: '', + private_key_passphrase: null, private_key: '', username: 'user', credential_store_id: 'csst_i7p1eu0Nw8',