diff --git a/addons/core/addon/decorators/notify.js b/addons/core/addon/decorators/notify.js index 42204023f..1ce13d91e 100644 --- a/addons/core/addon/decorators/notify.js +++ b/addons/core/addon/decorators/notify.js @@ -50,25 +50,22 @@ export function notifyError(notification, options = { catch: false }) { desc.value = async function () { const owner = getOwner(this); const notifyService = owner.lookup('service:notify'); - //const intlService = owner.lookup('service:intl'); + const intlService = owner.lookup('service:intl'); + try { return await method.apply(this, arguments); } catch (error) { - // As of now all error notifications pass a function, but this code - // could support either a function or a string. - // const candidateKey = (typeof notification === 'function') - // ? notification.apply(this, [error]) - // : notification; - const candidateKey = notification.apply(this, [error]); - // As of now, we only use untranslated strings for error notifications, - // but this code would support both translated and arbitrary strings. - // const text = intlService.exists(candidateKey) - // ? intlService.t(candidateKey) - // : candidateKey; - const text = candidateKey; + const candidateKey = (typeof notification === 'function') + ? notification.apply(this, [error]) + : notification; + + const text = intlService.exists(candidateKey) + ? intlService.t(candidateKey) + : candidateKey; notifyService.error(text, { closeAfter: null }); + if (options.catch) { // squelch the error } else { diff --git a/addons/rose/addon/components/rose/dropdown/link/index.hbs b/addons/rose/addon/components/rose/dropdown/link/index.hbs index 0213454f6..f4c2623f8 100644 --- a/addons/rose/addon/components/rose/dropdown/link/index.hbs +++ b/addons/rose/addon/components/rose/dropdown/link/index.hbs @@ -2,6 +2,7 @@ @@ -11,6 +12,7 @@ {{yield}} diff --git a/ui/admin/app/components/form/auth-method/index.hbs b/ui/admin/app/components/form/auth-method/index.hbs index 2704507ec..907940056 100644 --- a/ui/admin/app/components/form/auth-method/index.hbs +++ b/ui/admin/app/components/form/auth-method/index.hbs @@ -1,57 +1,7 @@ - - - - - {{#if @model.errors.name}} - - {{#each @model.errors.name as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - - - - {{#if @model.errors.description}} - - {{#each @model.errors.description as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - - - - +{{#if @model.type}} + {{component (concat "form/auth-method/" @model.type) + model=@model + submit=@submit + cancel=@cancel + }} +{{/if}} diff --git a/ui/admin/app/components/form/auth-method/password/index.hbs b/ui/admin/app/components/form/auth-method/password/index.hbs new file mode 100644 index 000000000..2704507ec --- /dev/null +++ b/ui/admin/app/components/form/auth-method/password/index.hbs @@ -0,0 +1,57 @@ + + + + + {{#if @model.errors.name}} + + {{#each @model.errors.name as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + {{#if @model.errors.description}} + + {{#each @model.errors.description as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + diff --git a/ui/admin/app/components/form/authenticate/index.hbs b/ui/admin/app/components/form/authenticate/index.hbs new file mode 100644 index 000000000..e7ca752c2 --- /dev/null +++ b/ui/admin/app/components/form/authenticate/index.hbs @@ -0,0 +1,7 @@ +{{#if @model.type}} + {{component (concat "form/authenticate/" @model.type) + submit=@submit + cancel=false + disabled=@disabled + }} +{{/if}} diff --git a/ui/admin/app/components/form/authenticate/password/index.hbs b/ui/admin/app/components/form/authenticate/password/index.hbs new file mode 100644 index 000000000..ee47910d8 --- /dev/null +++ b/ui/admin/app/components/form/authenticate/password/index.hbs @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/ui/admin/app/components/form/authenticate/password/index.js b/ui/admin/app/components/form/authenticate/password/index.js new file mode 100644 index 000000000..d6336f382 --- /dev/null +++ b/ui/admin/app/components/form/authenticate/password/index.js @@ -0,0 +1,37 @@ +import Component from '@glimmer/component'; +import { tracked } from '@glimmer/tracking'; +import { action } from '@ember/object'; +import { run } from '@ember/runloop'; + +export default class FormAuthenticatePasswordComponent extends Component { + // =properties + + /** + * New password property + * @type {string} + */ + @tracked password; + + // =methods + + /** + * Unsets the password field. + */ + resetPassword() { + this.password = null; + } + + // =actions + + /** + * Submit credentials, but reset the password first in case of failure. + * Callback with no arguments otherwise. + * @param {function} fn + * @param {object} creds + */ + @action + submit(fn, creds) { + run(() => this.resetPassword()); + run(() => fn(creds)); + } +} diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/new.js b/ui/admin/app/controllers/scopes/scope/auth-methods/new.js index ba550f456..4e562ae91 100644 --- a/ui/admin/app/controllers/scopes/scope/auth-methods/new.js +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/new.js @@ -8,6 +8,8 @@ export default class ScopesScopeAuthMethodsNewController extends Controller { // =attributes + queryParams = ['type']; + /** * Translated new auth-method breadcrumb * @type {string} diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/new.js b/ui/admin/app/routes/scopes/scope/auth-methods/new.js index dca9fcbdb..04ab0aacf 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/new.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/new.js @@ -1,17 +1,25 @@ import Route from '@ember/routing/route'; export default class ScopesScopeAuthMethodsNewRoute extends Route { + // =attributes + + queryParams = { + type: { + refreshModel: true + } + }; + // =methods /** * Create a new unsaved auth-method. * @return {AuthMethodModel} */ - model() { + model(params) { const scopeModel = this.modelFor('scopes.scope'); return this.store.createRecord('auth-method', { scopeModel, - type: 'password', + type: params.type, }); } } diff --git a/ui/admin/app/routes/scopes/scope/authenticate/method.js b/ui/admin/app/routes/scopes/scope/authenticate/method.js index 7f9f87856..cfb2c5acf 100644 --- a/ui/admin/app/routes/scopes/scope/authenticate/method.js +++ b/ui/admin/app/routes/scopes/scope/authenticate/method.js @@ -2,6 +2,7 @@ import Route from '@ember/routing/route'; import { inject as service } from '@ember/service'; import { action } from '@ember/object'; import loading from 'ember-loading/decorator'; +import { notifyError } from 'core/decorators/notify'; export default class ScopesScopeAuthenticateMethodRoute extends Route { // =services @@ -22,41 +23,26 @@ export default class ScopesScopeAuthenticateMethodRoute extends Route { return this.store.findRecord('auth-method', id, adapterOptions); } - /** - * Resets the password field on the controller so it is not - * inadvertantly retained in memory. - */ - resetCredentials() { - this.controller.setProperties({ - password: null, - }); - } - // =actions /** - * Delegates authentication to the `password` authenticator. - * If authentication fails, notifies with an alert. + * Delegates authentication to an authenticator of type matching the current + * auth method. If authentication fails, notifies with an alert. */ @action @loading + @notifyError(() => 'errors.authentication-failed.title', { catch: true }) async authenticate(creds) { - this.resetCredentials(); - const authenticatorName = 'authenticator:password'; const scope = this.modelFor('scopes.scope'); - const authMethod = this.currentModel; + const authMethod = this.modelFor('scopes.scope.authenticate.method'); + const authenticatorName = `authenticator:${authMethod.type}`; const requestCookies = false; - try { - await this.session.authenticate( - authenticatorName, - creds, - requestCookies, - { scope, authMethod } - ); - this.transitionTo('index'); - } catch (e) { - const errorMessage = this.intl.t('errors.authentication-failed.title'); - this.notify.error(errorMessage, { closeAfter: null }); - } + await this.session.authenticate( + authenticatorName, + creds, + requestCookies, + { scope, authMethod } + ); + this.transitionTo('index'); } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs index a796c66f5..0d10df1b5 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/index.hbs @@ -13,9 +13,17 @@ {{#if @model}} - - {{t "titles.new"}} - + + + {{t 'resources.auth-method.types.password'}} + + {{/if}} diff --git a/ui/admin/app/templates/scopes/scope/authenticate.hbs b/ui/admin/app/templates/scopes/scope/authenticate.hbs index 68d98634c..49af65143 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate.hbs @@ -29,7 +29,11 @@ {{#each @model.authMethods as |authMethod|}} - {{t (concat "resources.auth-method.types." authMethod.type)}} + {{#if authMethod.name}} + {{authMethod.name}} + {{else}} + {{t (concat "resources.auth-method.types." authMethod.type)}} + {{/if}} {{/each}} diff --git a/ui/admin/app/templates/scopes/scope/authenticate/method.hbs b/ui/admin/app/templates/scopes/scope/authenticate/method.hbs index 709c1f491..6428c8118 100644 --- a/ui/admin/app/templates/scopes/scope/authenticate/method.hbs +++ b/ui/admin/app/templates/scopes/scope/authenticate/method.hbs @@ -1,28 +1,5 @@ - - - - - - - - - +/> diff --git a/ui/admin/tests/acceptance/auth-methods-test.js b/ui/admin/tests/acceptance/auth-methods-test.js index b6d4bd912..bdc509df2 100644 --- a/ui/admin/tests/acceptance/auth-methods-test.js +++ b/ui/admin/tests/acceptance/auth-methods-test.js @@ -44,7 +44,7 @@ module('Acceptance | auth methods', function (hooks) { // Generate route URLs for resources urls.orgScope = `/scopes/${instances.scopes.org.id}`; urls.authMethods = `${urls.orgScope}/auth-methods`; - urls.newAuthMethod = `${urls.authMethods}/new`; + urls.newAuthMethod = `${urls.authMethods}/new?type=password`; urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; });