diff --git a/addons/api/addon/models/account.js b/addons/api/addon/models/account.js index 76321d86d..0d42a601d 100644 --- a/addons/api/addon/models/account.js +++ b/addons/api/addon/models/account.js @@ -9,4 +9,27 @@ export default class AccountModel extends GeneratedAccountModel { */ @fragment('fragment-account-attributes', { defaultValue: {} }) attributes; + // =methods + + /** + * Save account password via the `set-password` method. + * See serializer and adapter for more information. + * @param {object} options + * @param {object} options.adapterOptions + * @return {Promise} + */ + savePassword(options={ adapterOptions: {} }) { + const defaultAdapterOptions = { + method: 'set-password', + serializePassword: true + }; + return this.save({ + ...options, + adapterOptions: { + ...defaultAdapterOptions, + ...options.adapterOptions + } + }); + } + } diff --git a/addons/api/addon/serializers/account.js b/addons/api/addon/serializers/account.js new file mode 100644 index 000000000..361f835eb --- /dev/null +++ b/addons/api/addon/serializers/account.js @@ -0,0 +1,34 @@ +import ApplicationSerializer from './application'; +import { get } from '@ember/object'; + +export default class AccountSerializer extends ApplicationSerializer { + + // =methods + + /** + * If `adapterOptions.serializePassword` is true, the serialization should + * include **password** and the version. + * @override + * @param {Snapshot} snapshot + * @return {object} + */ + serialize(snapshot) { + const serializePassword = get(snapshot, 'adapterOptions.serializePassword'); + let serialized = super.serialize(...arguments); + if (serializePassword) serialized = this.serializeWithPassword(snapshot); + return serialized; + } + + /** + * Returns a payload containing only password. + * @param {Snapshot} snapshot + * @return {object} + */ + serializeWithPassword(snapshot) { + return { + version: snapshot.attr('version'), + password: snapshot.attr('attributes').attr('password'), + }; + } + +} diff --git a/addons/api/app/serializers/account.js b/addons/api/app/serializers/account.js new file mode 100644 index 000000000..af9b2cc1c --- /dev/null +++ b/addons/api/app/serializers/account.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/account'; diff --git a/addons/api/tests/unit/serializers/account-test.js b/addons/api/tests/unit/serializers/account-test.js new file mode 100644 index 000000000..6906c458d --- /dev/null +++ b/addons/api/tests/unit/serializers/account-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | account', function(hooks) { + setupTest(hooks); + + // Replace this with your real tests. + test('it exists', function(assert) { + let store = this.owner.lookup('service:store'); + let serializer = store.serializerFor('account'); + + assert.ok(serializer); + }); + + test('it serializes records', function(assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('account', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); diff --git a/ui/core/app/components/form/account/password/settings.hbs b/ui/core/app/components/form/account/password/settings.hbs new file mode 100644 index 000000000..3d48a1933 --- /dev/null +++ b/ui/core/app/components/form/account/password/settings.hbs @@ -0,0 +1,32 @@ + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + \ No newline at end of file diff --git a/ui/core/app/router.js b/ui/core/app/router.js index d0b1980f6..289f04d6f 100644 --- a/ui/core/app/router.js +++ b/ui/core/app/router.js @@ -74,7 +74,9 @@ Router.map(function () { this.route('auth-methods', function() { this.route('auth-method', { path: ':auth_method_id' }, function() { this.route('accounts', function() { - this.route('account', { path: ':account_id' }, function() {}); + this.route('account', { path: ':account_id' }, function() { + this.route('settings'); + }); this.route('new'); }); }); diff --git a/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings.js b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings.js new file mode 100644 index 000000000..115becc12 --- /dev/null +++ b/ui/core/app/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings.js @@ -0,0 +1,39 @@ +import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export default class ScopesScopeAuthMethodsAuthMethodAccountsAccountSettingsRoute extends Route { + + // =services + + @service intl; + @service notify; + + // =actions + + /** + * Save password for current account. + * @param {AccountModel} account + */ + @action + async savePassword(account) { + try { + await account.savePassword(); + await this.replaceWith('scopes.scope.auth-methods.auth-method.accounts.account.settings'); + this.notify.success( + this.intl.t('notify.save-success') + ); + } catch (error) { + // TODO: replace with translated strings + this.notify.error(error.message, { closeAfter: null }); + } + } + + /** + * Redirect to hosts as if nothing ever happened. + */ + @action + cancel() { + this.replaceWith('scopes.scope.auth-methods.auth-method.accounts.account'); + } +} diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-navigation.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-navigation.hbs index 24e8754b8..a7b550c26 100644 --- a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-navigation.hbs +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/-navigation.hbs @@ -2,4 +2,7 @@ {{t 'titles.details'}} + + {{t 'titles.settings'}} + diff --git a/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/settings.hbs b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/settings.hbs new file mode 100644 index 000000000..348503f1b --- /dev/null +++ b/ui/core/app/templates/scopes/scope/auth-methods/auth-method/accounts/account/settings.hbs @@ -0,0 +1,7 @@ +

{{t 'descriptions.account-settings-overview'}}

+ + diff --git a/ui/core/mirage/config.js b/ui/core/mirage/config.js index 62cb242b7..f3a7cda5f 100644 --- a/ui/core/mirage/config.js +++ b/ui/core/mirage/config.js @@ -63,11 +63,12 @@ export default function() { const method = idMethod.split(':')[1]; const account = accounts.find(id); const updatedAttrs = { - version: attrs.version + version: attrs.version, + attributes: {} }; // Set new password if (method === 'set-password') { - updatedAttrs.password = accounts.attributes.password; + updatedAttrs.attributes.password = attrs.password; } return account.update(updatedAttrs); }); diff --git a/ui/core/tests/integration/components/form/account/password/settings-test.js b/ui/core/tests/integration/components/form/account/password/settings-test.js new file mode 100644 index 000000000..c905dd76d --- /dev/null +++ b/ui/core/tests/integration/components/form/account/password/settings-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupRenderingTest } from 'ember-qunit'; +import { render, find } from '@ember/test-helpers'; +import { hbs } from 'ember-cli-htmlbars'; + +module('Integration | Component | form/account/password/settings', function(hooks) { + setupRenderingTest(hooks); + + test('it renders', async function(assert) { + this.submit = () => {}; + this.cancel = () => {}; + this.model = { type: 'password' }; + + await render(hbs` + `); + + assert.ok(find('form')); + }); +}); diff --git a/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings-test.js b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings-test.js new file mode 100644 index 000000000..ec14be2fc --- /dev/null +++ b/ui/core/tests/unit/routes/scopes/scope/auth-methods/auth-method/accounts/account/settings-test.js @@ -0,0 +1,11 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Route | scopes/scope/auth-methods/auth-method/accounts/account/settings', function(hooks) { + setupTest(hooks); + + test('it exists', function(assert) { + let route = this.owner.lookup('route:scopes/scope/auth-methods/auth-method/accounts/account/settings'); + assert.ok(route); + }); +}); diff --git a/ui/core/translations/en-us.yaml b/ui/core/translations/en-us.yaml index f1ef3e9cd..ed7bbf422 100644 --- a/ui/core/translations/en-us.yaml +++ b/ui/core/translations/en-us.yaml @@ -133,6 +133,7 @@ descriptions: group-members-welcome: Users belonging to this group appear here. no-available-users: No users are available to add to this group. auth-method-accounts-welcome: No accounts are available in this auth method. + account-settings-overview: Set the password for current account. states: info: Info success: Success