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