From 5ea26dc7eafd014e648c358a252ff95022bc8dc4 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 25 May 2021 17:36:28 -0400 Subject: [PATCH 01/23] feature: add array fields to oidc form --- addons/core/translations/en-us.yaml | 2 + addons/core/translations/form/en-us.yaml | 30 ++ .../styles/rose/components/table/_table.scss | 8 +- .../form/auth-method/oidc/index.hbs | 273 +++++++++++++++++- .../components/form/auth-method/oidc/index.js | 17 ++ 5 files changed, 328 insertions(+), 2 deletions(-) create mode 100644 ui/admin/app/components/form/auth-method/oidc/index.js diff --git a/addons/core/translations/en-us.yaml b/addons/core/translations/en-us.yaml index 56621bf33..5370c7e75 100644 --- a/addons/core/translations/en-us.yaml +++ b/addons/core/translations/en-us.yaml @@ -27,6 +27,8 @@ titles: abandon-confirm: Discard unsaved changes? toggle-color-theme: Toggle Color Theme welcome-to-boundary: Welcome to Boundary + label: Label + value: Value descriptions: empty-set: There are no items to display yet. You may be able to add items or try back later. origin-initialization: To get started, please enter your origin URL diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 974b2d313..1345b4509 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -70,3 +70,33 @@ disable_discovered_config_validation: label: Disable discovered config validation help: > If the Authorization Server's discovered configuration contains values that do not match the configuration set on this auth method, this can be set to force the local configuration to override the discovered values. This is useful in the case of slow-changing or incorrect values announced by the Authorization Server. NOTE: For safety, not providing this value will be treated the same as "false"; that is, every time the method is updated or the state is changed, this value must be set to "true" or it will be disabled. +idp_ca_certs: + label: Certificates + help: Optional PEM-encoded X.509 CA certificates that can be used as trust anchors when connecting to an OIDC provider. +certificate: + label: Certificate +allowed_audiences: + label: Allowed Audiences + help: Allowed audience claims for this auth method. +claim: + label: Claim +claims_scopes: + label: Claims Scopes + help: Optional claims scopes that will be requested during authentication. +claims_scope: + label: Claims Scope +account_claim_maps: + label: Account Claim Maps + help: > + Optional claim maps from custom claims to the standard claims of sub, name and email. These maps are represented as key=value where the key equals the from_claim and the value equals the to_claim. For example "oid=sub". +account_claim_map: + label: Account Claim Map +from_claim: + label: From Claim +to_claim: + label: To Claim +signing_algorithms: + label: Signing Algorithms + help: The signing algorithms allowed for the auth method. +algorithm: + label: Algorithm diff --git a/addons/rose/addon/styles/rose/components/table/_table.scss b/addons/rose/addon/styles/rose/components/table/_table.scss index 81a815a24..c4a839020 100644 --- a/addons/rose/addon/styles/rose/components/table/_table.scss +++ b/addons/rose/addon/styles/rose/components/table/_table.scss @@ -11,13 +11,19 @@ } // This styles are add to stop the input to apply extra margin (default on forms) + .rose-form-input, .rose-form-input-field, .rose-form-select, - .rose-form-input { + .rose-form-select-field { margin: 0; width: 100%; } + // Fix margin issue with textarea elements inside tables + textarea { + display: block; + } + .rose-table-cell, .rose-table-header-cell { vertical-align: middle; diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index f157e5090..c7ac8ad4a 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -119,6 +119,277 @@ {{/if}} + {{! Signing Algorithms }} + + <:title>{{t 'form.signing_algorithms.label'}} + <:description>{{t 'form.signing_algorithms.help'}} + <:body> + + + + {{t 'titles.value'}} + {{t 'titles.actions'}} + + + + {{#each @model.attributes.signing_algorithms as |algorithm i|}} + + + + {{t 'form.algorithm.label'}} + + {{#each this.signingAlgorithms as |algorithm|}} + {{algorithm}} + {{/each}} + + {{#if @model.errors.attributes.signing_algorithms}} + + {{#each @model.errors.attributes.signing_algorithms as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + + + + + + {{! Certificates }} + + <:title>{{t 'form.idp_ca_certs.label'}} + <:description>{{t 'form.idp_ca_certs.help'}} + <:body> + + + + {{t 'titles.value'}} + {{t 'titles.actions'}} + + + + {{#each @model.attributes.idp_ca_certs as |cert i|}} + + + + {{t 'form.certificate.label'}} + + {{#if @model.errors.attributes.idp_ca_certs}} + + {{#each @model.errors.attributes.idp_ca_certs as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + + + + + + {{! Allowed Audiences }} + + <:title>{{t 'form.allowed_audiences.label'}} + <:description>{{t 'form.allowed_audiences.help'}} + <:body> + + + + {{t 'titles.value'}} + {{t 'titles.actions'}} + + + + {{#each @model.attributes.allowed_audiences as |audience i|}} + + + + {{t 'form.claim.label'}} + + {{#if @model.errors.attributes.allowed_audiences}} + + {{#each @model.errors.attributes.allowed_audiences as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + + + + + + {{! Claims Scopes }} + + <:title>{{t 'form.claims_scopes.label'}} + <:description>{{t 'form.claims_scopes.help'}} + <:body> + + + + {{t 'titles.value'}} + + + + + {{#each @model.attributes.claims_scopes as |claimsScope i|}} + + + + {{t 'form.claims_scope.label'}} + + {{#if @model.errors.attributes.claims_scopes}} + + {{#each @model.errors.attributes.claims_scopes as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + + + + + + {{! Account Claim Maps }} + + <:title>{{t 'form.account_claim_maps.label'}} + <:description>{{t 'form.account_claim_maps.help'}} + <:body> + + + + {{t 'form.from_claim.label'}} + {{t 'form.to_claim.label'}} + {{t 'titles.actions'}} + + + + {{#each @model.attributes.account_claim_maps as |claimMap i|}} + + + + {{t 'form.from_claim.label'}} + + {{#if @model.errors.attributes.account_claim_maps}} + + {{#each @model.errors.attributes.account_claim_maps as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'form.to_claim.label'}} + + {{#each this.toClaims as |toClaim|}} + {{toClaim}} + {{/each}} + + {{#if @model.errors.attributes.account_claim_maps}} + + {{#each @model.errors.attributes.account_claim_maps as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + + + + + - \ No newline at end of file + diff --git a/ui/admin/app/components/form/auth-method/oidc/index.js b/ui/admin/app/components/form/auth-method/oidc/index.js new file mode 100644 index 000000000..4e4ac6f8b --- /dev/null +++ b/ui/admin/app/components/form/auth-method/oidc/index.js @@ -0,0 +1,17 @@ +import Component from '@glimmer/component'; +import { options } from 'api/models/auth-method'; + +export default class FormAuthMethodOidcComponent extends Component { + + // =attributes + /** + * @type {object} + */ + signingAlgorithms = options.oidc.signing_algorithms; + + /** + * @type {object} + */ + toClaims = options.oidc.account_claim_maps.to; + +} From 8c20d2980619a556ccdfa43be89f082ffec1acbe Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 26 May 2021 15:28:41 -0400 Subject: [PATCH 02/23] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20remove=20arr?= =?UTF-8?q?ay=20item=20support=20to=20oidc=20form?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rose/addon/components/rose/form/index.hbs | 1 + .../rose/components/form/_fieldset.scss | 2 + .../app/components/form/auth-method/index.hbs | 1 + .../form/auth-method/oidc/index.hbs | 194 +++++++++++------- .../components/form/auth-method/oidc/index.js | 2 - .../app/routes/scopes/scope/auth-methods.js | 14 ++ ui/admin/app/styles/app.scss | 12 +- .../scope/auth-methods/auth-method/index.hbs | 1 + 8 files changed, 148 insertions(+), 79 deletions(-) diff --git a/addons/rose/addon/components/rose/form/index.hbs b/addons/rose/addon/components/rose/form/index.hbs index 80b027371..8c545ec22 100644 --- a/addons/rose/addon/components/rose/form/index.hbs +++ b/addons/rose/addon/components/rose/form/index.hbs @@ -30,5 +30,6 @@ cancel=this.handleCancel enableEdit=this.enableEdit ) + isEditable=this.isEditable )}} diff --git a/addons/rose/addon/styles/rose/components/form/_fieldset.scss b/addons/rose/addon/styles/rose/components/form/_fieldset.scss index 8696ad562..d0b35d2ea 100644 --- a/addons/rose/addon/styles/rose/components/form/_fieldset.scss +++ b/addons/rose/addon/styles/rose/components/form/_fieldset.scss @@ -2,6 +2,8 @@ @use '../../utilities/type'; .rose-form-fieldset { + margin-bottom: sizing.rems(m); + .rose-form-fieldset-title { @include type.type(l, bold); } diff --git a/ui/admin/app/components/form/auth-method/index.hbs b/ui/admin/app/components/form/auth-method/index.hbs index 478c2ea91..9ac27859f 100644 --- a/ui/admin/app/components/form/auth-method/index.hbs +++ b/ui/admin/app/components/form/auth-method/index.hbs @@ -4,5 +4,6 @@ model=@model submit=@submit cancel=@cancel + removeItemByIndex=@removeItemByIndex }} {{/if}} \ No newline at end of file diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index c7ac8ad4a..a0ababf3d 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -39,7 +39,6 @@ @label={{t 'form.description.label'}} @helperText={{t 'form.description.help'}} @error={{@model.errors.description}} - disabled={{@model.isSaving}} readonly={{false}} as |field| > @@ -124,7 +123,7 @@ <:title>{{t 'form.signing_algorithms.label'}} <:description>{{t 'form.signing_algorithms.help'}} <:body> - + {{t 'titles.value'}} @@ -140,16 +139,22 @@ @error={{@model.errors.attributes.signing_algorithms}} @contextual={{true}} @onChange={{fn (mut algorithm.value)}} - as |field|> + as |field| + > {{t 'form.algorithm.label'}} {{#each this.signingAlgorithms as |algorithm|}} - {{algorithm}} + {{algorithm}} {{/each}} {{#if @model.errors.attributes.signing_algorithms}} - {{#each @model.errors.attributes.signing_algorithms as |error|}} + {{#each + @model.errors.attributes.signing_algorithms + as |error| + }} {{error.message}} {{/each}} @@ -158,57 +163,14 @@ - {{t 'actions.remove'}} - - - - {{/each}} - - - - - - {{! Certificates }} - - <:title>{{t 'form.idp_ca_certs.label'}} - <:description>{{t 'form.idp_ca_certs.help'}} - <:body> - - - - {{t 'titles.value'}} - {{t 'titles.actions'}} - - - - {{#each @model.attributes.idp_ca_certs as |cert i|}} - - - - {{t 'form.certificate.label'}} - - {{#if @model.errors.attributes.idp_ca_certs}} - - {{#each @model.errors.attributes.idp_ca_certs as |error|}} - {{error.message}} - {{/each}} - - {{/if}} - - - - {{t 'actions.remove'}} @@ -225,7 +187,7 @@ <:title>{{t 'form.allowed_audiences.label'}} <:description>{{t 'form.allowed_audiences.help'}} <:body> - + {{t 'titles.value'}} @@ -241,12 +203,16 @@ @value={{audience.value}} @error={{@model.errors.attributes.allowed_audiences}} @contextual={{true}} - as |field|> + as |field| + > {{t 'form.claim.label'}} {{#if @model.errors.attributes.allowed_audiences}} - {{#each @model.errors.attributes.allowed_audiences as |error|}} + {{#each + @model.errors.attributes.allowed_audiences + as |error| + }} {{error.message}} {{/each}} @@ -255,9 +221,14 @@ {{t 'actions.remove'}} @@ -274,11 +245,11 @@ <:title>{{t 'form.claims_scopes.label'}} <:description>{{t 'form.claims_scopes.help'}} <:body> - + {{t 'titles.value'}} - + @@ -289,13 +260,17 @@ @type='text' @value={{claimsScope.value}} @error={{@model.errors.attributes.claims_scopes}} - - as |field|> + @contextual={{true}} + as |field| + > {{t 'form.claims_scope.label'}} {{#if @model.errors.attributes.claims_scopes}} - {{#each @model.errors.attributes.claims_scopes as |error|}} + {{#each + @model.errors.attributes.claims_scopes + as |error| + }} {{error.message}} {{/each}} @@ -304,9 +279,14 @@ {{t 'actions.remove'}} @@ -323,7 +303,7 @@ <:title>{{t 'form.account_claim_maps.label'}} <:description>{{t 'form.account_claim_maps.help'}} <:body> - + {{t 'form.from_claim.label'}} @@ -340,12 +320,16 @@ @value={{claimMap.from}} @error={{@model.errors.attributes.account_claim_maps}} @contextual={{true}} - as |field|> + as |field| + > {{t 'form.from_claim.label'}} {{#if @model.errors.attributes.account_claim_maps}} - {{#each @model.errors.attributes.account_claim_maps as |error|}} + {{#each + @model.errors.attributes.account_claim_maps + as |error| + }} {{error.message}} {{/each}} @@ -358,16 +342,22 @@ @error={{@model.errors.attributes.account_claim_maps}} @contextual={{true}} @onChange={{fn (mut claimMap.to)}} - as |field|> + as |field| + > {{t 'form.to_claim.label'}} {{#each this.toClaims as |toClaim|}} - {{toClaim}} + {{toClaim}} {{/each}} {{#if @model.errors.attributes.account_claim_maps}} - {{#each @model.errors.attributes.account_claim_maps as |error|}} + {{#each + @model.errors.attributes.account_claim_maps + as |error| + }} {{error.message}} {{/each}} @@ -376,9 +366,14 @@ {{t 'actions.remove'}} @@ -390,6 +385,61 @@ + {{! Certificates }} + + <:title>{{t 'form.idp_ca_certs.label'}} + <:description>{{t 'form.idp_ca_certs.help'}} + <:body> + + + + {{t 'titles.value'}} + {{t 'titles.actions'}} + + + + {{#each @model.attributes.idp_ca_certs as |cert i|}} + + + + {{t 'form.certificate.label'}} + + {{#if @model.errors.attributes.idp_ca_certs}} + + {{#each @model.errors.attributes.idp_ca_certs as |error|}} + {{error.message}} + {{/each}} + + {{/if}} + + + + + {{t 'actions.remove'}} + + + + {{/each}} + {{! Add field }} + + + + + - + \ No newline at end of file diff --git a/ui/admin/app/components/form/auth-method/oidc/index.js b/ui/admin/app/components/form/auth-method/oidc/index.js index 4e4ac6f8b..fd99fc353 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.js +++ b/ui/admin/app/components/form/auth-method/oidc/index.js @@ -2,7 +2,6 @@ import Component from '@glimmer/component'; import { options } from 'api/models/auth-method'; export default class FormAuthMethodOidcComponent extends Component { - // =attributes /** * @type {object} @@ -13,5 +12,4 @@ export default class FormAuthMethodOidcComponent extends Component { * @type {object} */ toClaims = options.oidc.account_claim_maps.to; - } diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js index 291a9573d..d432f9b9d 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods.js @@ -134,4 +134,18 @@ export default class ScopesScopeAuthMethodsRoute extends Route { } await authMethod.reload(); } + + /** + * Removes an item from array `property` at `index` on the + * passed `authMethod`. This is used to manage entries in fragment array + * fields such as `signing_algorithms`. + * @param {AuthMethodModel} authMethod + * @param {string} property + * @param {number} index + */ + @action + async removeItemByIndex(authMethod, property, index) { + const array = authMethod.get(property); + array.removeAt(index); + } } diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 420258319..9defaac7b 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -80,11 +80,13 @@ } } - fieldset { - margin-bottom: sizing.rems(m); - - legend { - @extend .rose-form-label; + .rose-form-fieldset { + .rose-table { + .rose-form-label { + position: absolute; + top: -9999px; + left: -9999px; + } } } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index b82869296..f610fc81e 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -2,4 +2,5 @@ @model={{@model}} @submit={{route-action 'save' @model}} @cancel={{route-action 'cancel' @model}} + @removeItemByIndex={{route-action 'removeItemByIndex' @model}} /> \ No newline at end of file From 9fe89c9b18205faae08c062a5bb28c885e0bb07b Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 26 May 2021 17:12:51 -0400 Subject: [PATCH 03/23] feature: add addItem functionality to array fields --- .../app/components/form/auth-method/index.hbs | 1 + .../form/auth-method/oidc/index.hbs | 117 +++++++++++++++++- .../components/form/auth-method/oidc/index.js | 5 + .../app/routes/scopes/scope/auth-methods.js | 14 +++ .../scope/auth-methods/auth-method/index.hbs | 1 + 5 files changed, 137 insertions(+), 1 deletion(-) diff --git a/ui/admin/app/components/form/auth-method/index.hbs b/ui/admin/app/components/form/auth-method/index.hbs index 9ac27859f..048be9d25 100644 --- a/ui/admin/app/components/form/auth-method/index.hbs +++ b/ui/admin/app/components/form/auth-method/index.hbs @@ -5,5 +5,6 @@ submit=@submit cancel=@cancel removeItemByIndex=@removeItemByIndex + addStringItem=@addStringItem }} {{/if}} \ No newline at end of file diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index a0ababf3d..8b82c6467 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -177,6 +177,44 @@ {{/each}} + + + + + + {{#each this.signingAlgorithms as |algorithm|}} + {{algorithm}} + {{/each}} + + + + + + {{t 'actions.add'}} + + + @@ -235,6 +273,33 @@ {{/each}} + + + + + + + + {{t 'actions.add'}} + + + @@ -293,6 +358,33 @@ {{/each}} + + + + + + + + {{t 'actions.add'}} + + + @@ -434,7 +526,30 @@ {{/each}} - {{! Add field }} + + + + + + + + {{t 'actions.add'}} + + + diff --git a/ui/admin/app/components/form/auth-method/oidc/index.js b/ui/admin/app/components/form/auth-method/oidc/index.js index fd99fc353..166e33561 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.js +++ b/ui/admin/app/components/form/auth-method/oidc/index.js @@ -12,4 +12,9 @@ export default class FormAuthMethodOidcComponent extends Component { * @type {object} */ toClaims = options.oidc.account_claim_maps.to; + + /** + * @type {string} + */ + newSigningAlgorithm = options.oidc.signing_algorithms[0]; } diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js index d432f9b9d..00666c1fb 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods.js @@ -148,4 +148,18 @@ export default class ScopesScopeAuthMethodsRoute extends Route { const array = authMethod.get(property); array.removeAt(index); } + + /** + * Adds a string item to array `property` on the passed `authMethod`. + * This is used to manage entries in fragment string array fields such + * as `signing_algorithms`. + * @param {AuthMethodModel} authMethod + * @param {string} property + * @param {string} value + */ + @action + async addStringItem(authMethod, property, value) { + const array = authMethod.get(property); + array.addObject({ value }); + } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index f610fc81e..2c477bb7c 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -3,4 +3,5 @@ @submit={{route-action 'save' @model}} @cancel={{route-action 'cancel' @model}} @removeItemByIndex={{route-action 'removeItemByIndex' @model}} + @addStringItem={{route-action 'addStringItem' @model}} /> \ No newline at end of file From e50d0ac00b8367714b0467109501b047a43bbcb4 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 26 May 2021 18:09:28 -0400 Subject: [PATCH 04/23] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20account=20cl?= =?UTF-8?q?aim=20map=20support?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/form/auth-method/index.hbs | 1 + .../form/auth-method/oidc/index.hbs | 39 +++++++++++++++++++ .../app/routes/scopes/scope/auth-methods.js | 15 ++++++- .../scope/auth-methods/auth-method/index.hbs | 1 + 4 files changed, 55 insertions(+), 1 deletion(-) diff --git a/ui/admin/app/components/form/auth-method/index.hbs b/ui/admin/app/components/form/auth-method/index.hbs index 048be9d25..77cf37d4d 100644 --- a/ui/admin/app/components/form/auth-method/index.hbs +++ b/ui/admin/app/components/form/auth-method/index.hbs @@ -6,5 +6,6 @@ cancel=@cancel removeItemByIndex=@removeItemByIndex addStringItem=@addStringItem + addAccountClaimMapItem=@addAccountClaimMapItem }} {{/if}} \ No newline at end of file diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index 8b82c6467..335392e08 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -472,6 +472,45 @@ {{/each}} + + + + + + + + + {{#each this.toClaims as |toClaim|}} + {{toClaim}} + {{/each}} + + + + + + {{t 'actions.add'}} + + + diff --git a/ui/admin/app/routes/scopes/scope/auth-methods.js b/ui/admin/app/routes/scopes/scope/auth-methods.js index 00666c1fb..31c0778a5 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods.js @@ -151,7 +151,7 @@ export default class ScopesScopeAuthMethodsRoute extends Route { /** * Adds a string item to array `property` on the passed `authMethod`. - * This is used to manage entries in fragment string array fields such + * This is used to manage entries in fragment OIDC string array fields such * as `signing_algorithms`. * @param {AuthMethodModel} authMethod * @param {string} property @@ -162,4 +162,17 @@ export default class ScopesScopeAuthMethodsRoute extends Route { const array = authMethod.get(property); array.addObject({ value }); } + + /** + * Adds an account claim map fragment to the passed OIDC `authMethod`. + * @param {AuthMethodModel} authMethod + * @param {string} property + * @param {string} value + */ + @action + async addAccountClaimMapItem(authMethod, from, to) { + const array = authMethod.attributes.account_claim_maps; + const value = { from, to }; + array.addObject(value); + } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs index 2c477bb7c..e3302df0b 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/index.hbs @@ -4,4 +4,5 @@ @cancel={{route-action 'cancel' @model}} @removeItemByIndex={{route-action 'removeItemByIndex' @model}} @addStringItem={{route-action 'addStringItem' @model}} + @addAccountClaimMapItem={{route-action 'addAccountClaimMapItem' @model}} /> \ No newline at end of file From c14be070f759ba0c4dedc39a5e129d39198af050 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 1 Jun 2021 22:29:19 -0400 Subject: [PATCH 05/23] feat: Add oidc status change dropdown to auth method actions --- addons/api/addon/models/auth-method.js | 4 +- addons/api/mirage/config.js | 29 +++- addons/api/mirage/route-handlers/auth.js | 2 +- addons/api/mirage/scenarios/default.js | 6 + addons/core/translations/resources/en-us.yaml | 1 + addons/core/translations/states/en-us.yaml | 6 + .../scopes/scope/auth-methods/auth-method.js | 16 +++ .../auth-methods/auth-method/-actions.hbs | 34 ++++- .../acceptance/auth-methods/oidc-test.js | 134 ++++++++++++++++++ 9 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 ui/admin/tests/acceptance/auth-methods/oidc-test.js diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index dc03abc0d..1ba8050a9 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -3,7 +3,7 @@ import { fragment } from 'ember-data-model-fragments/attributes'; import { equal } from '@ember/object/computed'; /** - * Enum ptions per auth method type and field. + * Enum options per auth method type and field. */ export const options = { oidc: { @@ -27,6 +27,8 @@ export const options = { }; export default class AuthMethodModel extends GeneratedAuthMethodModel { + STATES_ENUM = options.oidc.state; + // =attributes /** diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 769e7e810..d34110910 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import config from '../config/environment'; -import { authHandler, deauthHandler } from './route-handlers/auth'; +import { deauthHandler } from './route-handlers/auth'; import { pickRandomStatusString } from './factories/session'; import { Response } from 'miragejs'; import initializeMockIPC from './scenarios/ipc'; @@ -71,6 +71,31 @@ export default function () { } return new Response(202); }); + this.post('/auth-methods/:idMethod', function (schema, request) { + const idMethod = request.params.idMethod; + const id = idMethod.split(':')[0]; + const method = idMethod.split(':')[1]; + + if (method === 'authenticate') { + schema.authHandler.call(this, schema, request); + return; + } + + const attrs = this.normalizedRequestAttrs(); + const authMethods = schema.authMethods; + const authMethod = authMethods.find(id); + let updatedAttrs = {}; + + if (method === 'change-state') { + updatedAttrs = { + attributes: { + state: attrs.attributes.state, + }, + }; + } + + return authMethod.update(updatedAttrs); + }); // Auth Method Accounts this.get( @@ -107,7 +132,7 @@ export default function () { ); // Authenticate/deauthenticate routes - this.post('/auth-methods/:id_method', authHandler); + // this.post('/auth-methods/:id_method', authHandler); this.post('/scopes/:id_method', deauthHandler); // IAM : Users diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index bd4fca7ac..522ac0b52 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -109,7 +109,7 @@ const commandHandlers = { export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - const [, id] = request.params.id_method.match(/(?.[^:]*)/); + const [, id] = request.params.idMethod.match(/(?.[^:]*)/); const { command } = payload; const authMethod = authMethods.find(id); const scope = scopes.find(authMethod.scopeId); diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index 62018788c..1f2595bb8 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -1,3 +1,5 @@ +import { authHandler } from '../route-handlers/auth'; + export default function (server) { // Scope resources @@ -50,4 +52,8 @@ export default function (server) { server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); }); + + // Authenticate route handler + // TODO: Better way to pass-in auth handler + server.schema.authHandler = authHandler; } diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 7432ac466..aa42e8d53 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -38,6 +38,7 @@ auth-method: delete: Delete Auth Method make-primary: Make Primary remove-as-primary: Remove as Primary + change-state: State notifications: make-primary-success: Auth method successfully elected to primary. remove-as-primary-success: Auth method successfully removed as primary. diff --git a/addons/core/translations/states/en-us.yaml b/addons/core/translations/states/en-us.yaml index aa8e846bd..23a92c6cf 100644 --- a/addons/core/translations/states/en-us.yaml +++ b/addons/core/translations/states/en-us.yaml @@ -8,3 +8,9 @@ connection: success: description: "Local proxy address ({protocol}): {address}:{port}" primary: Primary +auth-method: + oidc: + states: + inactive: Inactive + active-private: Active Private + active-public: Active Public diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js index 3c608f660..c492fed7b 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js @@ -1,4 +1,6 @@ import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import { notifySuccess, notifyError } from 'core/decorators/notify'; export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { // =methods @@ -36,4 +38,18 @@ export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { outlet: 'actions', }); } + + // =actions + + /** + * Update state of OIDC auth method + * @param {string} state - "inactive", "active-private", "active-public" + */ + @action + @notifyError(({ message }) => message) + @notifySuccess('notifications.save-success') + async changeState(state) { + const model = this.modelFor('scopes.scope.auth-methods.auth-method'); + await model.changeState(state); + } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index c46e656f8..5b05d127a 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -1,3 +1,35 @@ +{{#if @model.isOIDC}} + + + + + {{#each @model.STATES_ENUM as |state|}} + + + + {{/each}} + + + + +{{/if}} + {{t 'resources.auth-method.actions.delete'}} - \ No newline at end of file + diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js new file mode 100644 index 000000000..75b89edeb --- /dev/null +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -0,0 +1,134 @@ +import { module, test } from 'qunit'; +import { + visit, + currentURL, + click, + // fillIn, + find, + // findAll, +} from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | auth methods | oidc', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + }; + const urls = { + orgScope: null, + authMethods: null, + authMethod: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + }); + + test('visiting oidc auth method', async function (assert) { + assert.expect(1); + await visit(urls.authMethod); + await a11yAudit(); + assert.equal(currentURL(), urls.authMethod); + }); + + test('can view oidc state', async function(assert) { + assert.expect(1); + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + assert.equal( + find('.rose-dropdown[open] input[aria-checked=true]').value, + instances.authMethod.attributes.state + ); + }); + + test('can update oidc state', async function(assert) { + assert.expect(1); + const updateValue = 'inactive'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('can update oidc state to active-private', async function(assert) { + assert.expect(1); + const updateValue = 'active-private'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('can update oidc state to active-public', async function(assert) { + assert.expect(1); + // Update default 'active-public' state to inactive + instances.authMethod.attributes.state = 'inactive'; + const updateValue = 'active-public'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('errors are displayed when state update fails', async function (assert) { + assert.expect(2); + const newState = 'inactive'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + // Load page before mocking update + this.server.post('/auth-methods/:idMethod', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'error', + message: 'Sorry!', + } + ); + }); + await click(`.rose-dropdown[open] input[value="${newState}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.notEqual( + newState, + authMethod.attributes.state, + 'Auth method state is not be updated.' + ); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Sorry!', + 'Displays error message.' + ); + }); +}); From 31f67e568c2613a270b38ed2f97c66849a25df2a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 2 Jun 2021 12:09:17 -0400 Subject: [PATCH 06/23] chore: add missing translations --- addons/core/translations/form/en-us.yaml | 2 +- addons/core/translations/resources/en-us.yaml | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 1345b4509..65277af78 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -59,7 +59,7 @@ client_id: help: An OAuth 2.0 Client Identifier valid at the Authorization Server. client_secret: label: Client Secret - description: The client secret is set-only, may not be unset, and will not appear after a successful save. + help: The client secret is set-only and may not be unset. max_age: label: Maximum Age help: The OIDC "max_age" parameter sent to the Authorization Server indicating a maximum acceptable time in seconds since the user's last authentication before requiring the user to reauthenticate. 0 indicates an immediate need to reauthenticate. diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 7432ac466..8ab5152ac 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -30,6 +30,13 @@ auth-method: pending: title: Authentication Pending description: Complete authentication in the newly-opened window. + titles: + new-signing-algorithm: New Signing Algorithm + new-allowed-audience: New Allowed Audience + new-claims-scope: New Claims Scope + new-from-claim: New From Claim + new-to-claim: New To Claim + new-certificate: New Certificate types: password: Password oidc: OIDC From 683fffaa0e9d31fe48d3292abeb392f031c5ad23 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 2 Jun 2021 12:09:34 -0400 Subject: [PATCH 07/23] fix: correct path to logo-app --- ui/admin/app/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/app/index.html b/ui/admin/app/index.html index a56a21e63..1af9cc9af 100644 --- a/ui/admin/app/index.html +++ b/ui/admin/app/index.html @@ -12,7 +12,7 @@ - + {{content-for "head-footer"}} From c013b178d7e2232b54261d96059cd5c889f32dc6 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 2 Jun 2021 12:38:15 -0400 Subject: [PATCH 08/23] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20oidc-account?= =?UTF-8?q?-crud=20feature=20flag?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../scope/auth-methods/auth-method/-navigation.hbs | 14 +++++++++++--- ui/admin/config/environment.js | 4 +++- 2 files changed, 14 insertions(+), 4 deletions(-) diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs index 568445801..2694041c8 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-navigation.hbs @@ -2,7 +2,15 @@ {{t 'titles.details'}} - - {{t 'resources.account.title_plural'}} - + {{#if @model.isOIDC}} + {{#if (feature-flag 'oidc-account-crud')}} + + {{t 'resources.account.title_plural'}} + + {{/if}} + {{else}} + + {{t 'resources.account.title_plural'}} + + {{/if}} \ No newline at end of file diff --git a/ui/admin/config/environment.js b/ui/admin/config/environment.js index 21d1e3da6..1e9db7322 100644 --- a/ui/admin/config/environment.js +++ b/ui/admin/config/environment.js @@ -88,7 +88,8 @@ module.exports = function (environment) { featureFlags: { 'primary-auth-method': true, oidc: true, - 'oidc-crud': false, + 'oidc-crud': true, + 'oidc-account-crud': false, search: false, filter: false, capabilities: false, @@ -131,6 +132,7 @@ module.exports = function (environment) { ENV.featureFlags['primary-auth-method'] = true; ENV.featureFlags['oidc'] = true; ENV.featureFlags['oidc-crud'] = true; + //ENV.featureFlags['oidc-account-crud'] = true; } if (environment === 'test') { From c41855ccc41de4f9ea0d57c44f93f68808e222b1 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 25 May 2021 14:52:20 -0400 Subject: [PATCH 09/23] =?UTF-8?q?chore:=20=F0=9F=A4=96=20fix=20imports=20t?= =?UTF-8?q?hat=20don't=20make=20sense?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/core/app/helpers/app-name.js | 2 +- addons/core/app/helpers/company-copyright.js | 2 +- addons/core/app/helpers/company-name.js | 2 +- addons/core/app/helpers/doc-url.js | 2 +- .../core/app/helpers/format-date-iso-human.js | 5 +-- addons/core/app/helpers/format-date-iso.js | 2 +- addons/core/app/helpers/is-loading.js | 2 +- .../rose/app/components/rose/layout/tabs.js | 1 - addons/rose/app/components/rose/tab.js | 1 - yarn.lock | 33 ++++++++++++++----- 10 files changed, 32 insertions(+), 20 deletions(-) delete mode 100644 addons/rose/app/components/rose/layout/tabs.js delete mode 100644 addons/rose/app/components/rose/tab.js diff --git a/addons/core/app/helpers/app-name.js b/addons/core/app/helpers/app-name.js index ee117ad2f..6caf08d2e 100644 --- a/addons/core/app/helpers/app-name.js +++ b/addons/core/app/helpers/app-name.js @@ -1 +1 @@ -export { default, appName } from 'core/helpers/app-name'; +export { default } from 'core/helpers/app-name'; diff --git a/addons/core/app/helpers/company-copyright.js b/addons/core/app/helpers/company-copyright.js index d76da80c8..c846d26c1 100644 --- a/addons/core/app/helpers/company-copyright.js +++ b/addons/core/app/helpers/company-copyright.js @@ -1 +1 @@ -export { default, companyCopyright } from 'core/helpers/company-copyright'; +export { default } from 'core/helpers/company-copyright'; diff --git a/addons/core/app/helpers/company-name.js b/addons/core/app/helpers/company-name.js index da699e089..01921c115 100644 --- a/addons/core/app/helpers/company-name.js +++ b/addons/core/app/helpers/company-name.js @@ -1 +1 @@ -export { default, companyName } from 'core/helpers/company-name'; +export { default } from 'core/helpers/company-name'; diff --git a/addons/core/app/helpers/doc-url.js b/addons/core/app/helpers/doc-url.js index a8f150ce1..ae356804f 100644 --- a/addons/core/app/helpers/doc-url.js +++ b/addons/core/app/helpers/doc-url.js @@ -1 +1 @@ -export { default, docUrl } from 'core/helpers/doc-url'; +export { default } from 'core/helpers/doc-url'; diff --git a/addons/core/app/helpers/format-date-iso-human.js b/addons/core/app/helpers/format-date-iso-human.js index 947f5e926..3ed9c0ca2 100644 --- a/addons/core/app/helpers/format-date-iso-human.js +++ b/addons/core/app/helpers/format-date-iso-human.js @@ -1,4 +1 @@ -export { - default, - formatDateIsoHuman, -} from 'core/helpers/format-date-iso-human'; +export { default } from 'core/helpers/format-date-iso-human'; diff --git a/addons/core/app/helpers/format-date-iso.js b/addons/core/app/helpers/format-date-iso.js index f82d799e1..ef7aab468 100644 --- a/addons/core/app/helpers/format-date-iso.js +++ b/addons/core/app/helpers/format-date-iso.js @@ -1 +1 @@ -export { default, formatDateIso } from 'core/helpers/format-date-iso'; +export { default } from 'core/helpers/format-date-iso'; diff --git a/addons/core/app/helpers/is-loading.js b/addons/core/app/helpers/is-loading.js index 0645984c8..27b232c3d 100644 --- a/addons/core/app/helpers/is-loading.js +++ b/addons/core/app/helpers/is-loading.js @@ -1 +1 @@ -export { default, isLoading } from 'core/helpers/is-loading'; +export { default } from 'core/helpers/is-loading'; diff --git a/addons/rose/app/components/rose/layout/tabs.js b/addons/rose/app/components/rose/layout/tabs.js deleted file mode 100644 index 9de968192..000000000 --- a/addons/rose/app/components/rose/layout/tabs.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'rose/components/rose/layout/tabs'; diff --git a/addons/rose/app/components/rose/tab.js b/addons/rose/app/components/rose/tab.js deleted file mode 100644 index 37b6e680c..000000000 --- a/addons/rose/app/components/rose/tab.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'rose/components/rose/tab'; diff --git a/yarn.lock b/yarn.lock index f01866ca4..5ec5ecb54 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7560,7 +7560,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@^1.0.1: +debuglog@*, debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -12369,7 +12369,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@^0.1.4: +imurmurhash@*, imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -14067,6 +14067,11 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" +lodash._baseindexof@*: + version "3.1.0" + resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" + integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= + lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14075,11 +14080,16 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@^3.0.0: +lodash._bindcallback@*, lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= +lodash._cacheindexof@*: + version "3.0.2" + resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" + integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= + lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14089,12 +14099,19 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" +lodash._createcache@*: + version "3.1.2" + resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" + integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= + dependencies: + lodash._getnative "^3.0.0" + lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@^3.0.0: +lodash._getnative@*, lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14312,7 +14329,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@^3.0.0: +lodash.restparam@*, lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= @@ -21816,9 +21833,9 @@ xmldom@^0.5.0: integrity sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA== xmlhttprequest-ssl@^1.6.2, xmlhttprequest-ssl@~1.5.4: - version "1.6.2" - resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.2.tgz#dd6899bfbcf684b554e393c30b13b9f3b001a7ee" - integrity sha512-tYOaldF/0BLfKuoA39QMwD4j2m8lq4DIncqj1yuNELX4vz9+z/ieG/vwmctjJce+boFHXstqhWnHSxc4W8f4qg== + version "1.6.3" + resolved "https://registry.yarnpkg.com/xmlhttprequest-ssl/-/xmlhttprequest-ssl-1.6.3.tgz#03b713873b01659dfa2c1c5d056065b27ddc2de6" + integrity sha512-3XfeQE/wNkvrIktn2Kf0869fC0BN6UpydVasGIeSm2B1Llihf7/0UfZM+eCkOw3P7bP4+qPgqhm7ZoxuJtFU0Q== xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.0, xtend@~4.0.1: version "4.0.2" From 3a52b402144c49fb90cffbce160d039aef039efd Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Tue, 25 May 2021 13:14:32 -0400 Subject: [PATCH 10/23] chore: add meta description --- ui/admin/app/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/app/index.html b/ui/admin/app/index.html index 1af9cc9af..befb5d7b5 100644 --- a/ui/admin/app/index.html +++ b/ui/admin/app/index.html @@ -4,7 +4,7 @@ Admin - + {{content-for "head"}} From bbd73b36d1ad213eee24e5b63ac128021203d869 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 1 Jun 2021 12:55:35 -0400 Subject: [PATCH 11/23] chore: Fix js lint errors --- addons/api/addon/services/storage.js | 5 +++-- addons/auth/addon/authenticators/base.js | 7 +++---- addons/core/addon/authenticators/oidc.js | 4 ++-- addons/core/addon/authenticators/password.js | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/api/addon/services/storage.js b/addons/api/addon/services/storage.js index ea1b7138d..d9b7aa495 100644 --- a/addons/api/addon/services/storage.js +++ b/addons/api/addon/services/storage.js @@ -76,8 +76,9 @@ export default class StorageService extends Service { const inMemory = get(config, 'storage.memory'); // Get the global context object (window) via service lookup, // because just reaching for a global is icky - const globalContext = getOwner(this).lookup('service:-document') - .documentElement.parentNode.defaultView; + const globalContext = + getOwner(this).lookup('service:-document').documentElement.parentNode + .defaultView; // If memory storage enabled, return the memory storage instance, // otherwise return localStorage. return inMemory ? this.#memoryStorage : globalContext.localStorage; diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index bdc69d504..deeea5a0d 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -72,10 +72,9 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { */ async restore(data) { if (!data) return reject(); - return this.validateToken( - data.attributes.token, - data.attributes.id - ).then(() => this.normalizeData(data)); + return this.validateToken(data.attributes.token, data.attributes.id).then( + () => this.normalizeData(data) + ); } /** diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index f159fa87f..245dc97cd 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -76,8 +76,8 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { * @param {string} token */ addTokenToAuthorization(token) { - const adapterPrototype = this.store.adapterFor('application').constructor - .prototype; + const adapterPrototype = + this.store.adapterFor('application').constructor.prototype; const headers = adapterPrototype?.headers; if (!headers) adapterPrototype.headers = {}; adapterPrototype.headers.Authorization = null; diff --git a/addons/core/addon/authenticators/password.js b/addons/core/addon/authenticators/password.js index 186731a7d..a2dfc8131 100644 --- a/addons/core/addon/authenticators/password.js +++ b/addons/core/addon/authenticators/password.js @@ -91,8 +91,8 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} token */ addTokenToAuthorization(token) { - const adapterPrototype = this.store.adapterFor('application').constructor - .prototype; + const adapterPrototype = + this.store.adapterFor('application').constructor.prototype; const headers = adapterPrototype?.headers; if (!headers) adapterPrototype.headers = {}; adapterPrototype.headers.Authorization = null; From bc10b008749c4703d43f97d201a970f490a76fcb Mon Sep 17 00:00:00 2001 From: Carlos Alcaide Date: Tue, 1 Jun 2021 11:07:48 -0700 Subject: [PATCH 12/23] =?UTF-8?q?chore:=20=F0=9F=A4=96=20Fix=20lint=20erro?= =?UTF-8?q?rs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fix lint errors --- addons/api/addon/services/storage.js | 5 ++--- addons/auth/addon/authenticators/base.js | 7 ++++--- addons/core/addon/authenticators/oidc.js | 4 ++-- addons/core/addon/authenticators/password.js | 4 ++-- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/addons/api/addon/services/storage.js b/addons/api/addon/services/storage.js index d9b7aa495..ea1b7138d 100644 --- a/addons/api/addon/services/storage.js +++ b/addons/api/addon/services/storage.js @@ -76,9 +76,8 @@ export default class StorageService extends Service { const inMemory = get(config, 'storage.memory'); // Get the global context object (window) via service lookup, // because just reaching for a global is icky - const globalContext = - getOwner(this).lookup('service:-document').documentElement.parentNode - .defaultView; + const globalContext = getOwner(this).lookup('service:-document') + .documentElement.parentNode.defaultView; // If memory storage enabled, return the memory storage instance, // otherwise return localStorage. return inMemory ? this.#memoryStorage : globalContext.localStorage; diff --git a/addons/auth/addon/authenticators/base.js b/addons/auth/addon/authenticators/base.js index deeea5a0d..bdc69d504 100644 --- a/addons/auth/addon/authenticators/base.js +++ b/addons/auth/addon/authenticators/base.js @@ -72,9 +72,10 @@ export default class BaseAuthenticator extends SimpleAuthBaseAuthenticator { */ async restore(data) { if (!data) return reject(); - return this.validateToken(data.attributes.token, data.attributes.id).then( - () => this.normalizeData(data) - ); + return this.validateToken( + data.attributes.token, + data.attributes.id + ).then(() => this.normalizeData(data)); } /** diff --git a/addons/core/addon/authenticators/oidc.js b/addons/core/addon/authenticators/oidc.js index 245dc97cd..f159fa87f 100644 --- a/addons/core/addon/authenticators/oidc.js +++ b/addons/core/addon/authenticators/oidc.js @@ -76,8 +76,8 @@ export default class OIDCAuthenticator extends BaseOIDCAuthenticator { * @param {string} token */ addTokenToAuthorization(token) { - const adapterPrototype = - this.store.adapterFor('application').constructor.prototype; + const adapterPrototype = this.store.adapterFor('application').constructor + .prototype; const headers = adapterPrototype?.headers; if (!headers) adapterPrototype.headers = {}; adapterPrototype.headers.Authorization = null; diff --git a/addons/core/addon/authenticators/password.js b/addons/core/addon/authenticators/password.js index a2dfc8131..186731a7d 100644 --- a/addons/core/addon/authenticators/password.js +++ b/addons/core/addon/authenticators/password.js @@ -91,8 +91,8 @@ export default class PasswordAuthenticator extends BasePasswordAuthenticator { * @param {string} token */ addTokenToAuthorization(token) { - const adapterPrototype = - this.store.adapterFor('application').constructor.prototype; + const adapterPrototype = this.store.adapterFor('application').constructor + .prototype; const headers = adapterPrototype?.headers; if (!headers) adapterPrototype.headers = {}; adapterPrototype.headers.Authorization = null; From 44fa206619426665fa86d78387a3c486cad38260 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Tue, 1 Jun 2021 22:29:19 -0400 Subject: [PATCH 13/23] feat: Add oidc status change dropdown to auth method actions --- addons/api/addon/models/auth-method.js | 4 +- addons/api/mirage/config.js | 29 +++- addons/api/mirage/route-handlers/auth.js | 2 +- addons/api/mirage/scenarios/default.js | 6 + addons/core/translations/resources/en-us.yaml | 1 + addons/core/translations/states/en-us.yaml | 6 + .../scopes/scope/auth-methods/auth-method.js | 16 +++ .../auth-methods/auth-method/-actions.hbs | 34 ++++- .../acceptance/auth-methods/oidc-test.js | 134 ++++++++++++++++++ 9 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 ui/admin/tests/acceptance/auth-methods/oidc-test.js diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index dc03abc0d..1ba8050a9 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -3,7 +3,7 @@ import { fragment } from 'ember-data-model-fragments/attributes'; import { equal } from '@ember/object/computed'; /** - * Enum ptions per auth method type and field. + * Enum options per auth method type and field. */ export const options = { oidc: { @@ -27,6 +27,8 @@ export const options = { }; export default class AuthMethodModel extends GeneratedAuthMethodModel { + STATES_ENUM = options.oidc.state; + // =attributes /** diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 769e7e810..d34110910 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import config from '../config/environment'; -import { authHandler, deauthHandler } from './route-handlers/auth'; +import { deauthHandler } from './route-handlers/auth'; import { pickRandomStatusString } from './factories/session'; import { Response } from 'miragejs'; import initializeMockIPC from './scenarios/ipc'; @@ -71,6 +71,31 @@ export default function () { } return new Response(202); }); + this.post('/auth-methods/:idMethod', function (schema, request) { + const idMethod = request.params.idMethod; + const id = idMethod.split(':')[0]; + const method = idMethod.split(':')[1]; + + if (method === 'authenticate') { + schema.authHandler.call(this, schema, request); + return; + } + + const attrs = this.normalizedRequestAttrs(); + const authMethods = schema.authMethods; + const authMethod = authMethods.find(id); + let updatedAttrs = {}; + + if (method === 'change-state') { + updatedAttrs = { + attributes: { + state: attrs.attributes.state, + }, + }; + } + + return authMethod.update(updatedAttrs); + }); // Auth Method Accounts this.get( @@ -107,7 +132,7 @@ export default function () { ); // Authenticate/deauthenticate routes - this.post('/auth-methods/:id_method', authHandler); + // this.post('/auth-methods/:id_method', authHandler); this.post('/scopes/:id_method', deauthHandler); // IAM : Users diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index bd4fca7ac..522ac0b52 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -109,7 +109,7 @@ const commandHandlers = { export function authHandler({ scopes, authMethods }, request) { const payload = JSON.parse(request.requestBody); - const [, id] = request.params.id_method.match(/(?.[^:]*)/); + const [, id] = request.params.idMethod.match(/(?.[^:]*)/); const { command } = payload; const authMethod = authMethods.find(id); const scope = scopes.find(authMethod.scopeId); diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index 62018788c..1f2595bb8 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -1,3 +1,5 @@ +import { authHandler } from '../route-handlers/auth'; + export default function (server) { // Scope resources @@ -50,4 +52,8 @@ export default function (server) { server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); }); + + // Authenticate route handler + // TODO: Better way to pass-in auth handler + server.schema.authHandler = authHandler; } diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 8ab5152ac..0cab323c1 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -45,6 +45,7 @@ auth-method: delete: Delete Auth Method make-primary: Make Primary remove-as-primary: Remove as Primary + change-state: State notifications: make-primary-success: Auth method successfully elected to primary. remove-as-primary-success: Auth method successfully removed as primary. diff --git a/addons/core/translations/states/en-us.yaml b/addons/core/translations/states/en-us.yaml index aa8e846bd..23a92c6cf 100644 --- a/addons/core/translations/states/en-us.yaml +++ b/addons/core/translations/states/en-us.yaml @@ -8,3 +8,9 @@ connection: success: description: "Local proxy address ({protocol}): {address}:{port}" primary: Primary +auth-method: + oidc: + states: + inactive: Inactive + active-private: Active Private + active-public: Active Public diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js index 3c608f660..c492fed7b 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js @@ -1,4 +1,6 @@ import Route from '@ember/routing/route'; +import { action } from '@ember/object'; +import { notifySuccess, notifyError } from 'core/decorators/notify'; export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { // =methods @@ -36,4 +38,18 @@ export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { outlet: 'actions', }); } + + // =actions + + /** + * Update state of OIDC auth method + * @param {string} state - "inactive", "active-private", "active-public" + */ + @action + @notifyError(({ message }) => message) + @notifySuccess('notifications.save-success') + async changeState(state) { + const model = this.modelFor('scopes.scope.auth-methods.auth-method'); + await model.changeState(state); + } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index c46e656f8..5b05d127a 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -1,3 +1,35 @@ +{{#if @model.isOIDC}} + + + + + {{#each @model.STATES_ENUM as |state|}} + + + + {{/each}} + + + + +{{/if}} + {{t 'resources.auth-method.actions.delete'}} - \ No newline at end of file + diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js new file mode 100644 index 000000000..75b89edeb --- /dev/null +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -0,0 +1,134 @@ +import { module, test } from 'qunit'; +import { + visit, + currentURL, + click, + // fillIn, + find, + // findAll, +} from '@ember/test-helpers'; +import { setupApplicationTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import a11yAudit from 'ember-a11y-testing/test-support/audit'; +import { Response } from 'miragejs'; +import { + authenticateSession, + // These are left here intentionally for future reference. + //currentSession, + //invalidateSession, +} from 'ember-simple-auth/test-support'; + +module('Acceptance | auth methods | oidc', function (hooks) { + setupApplicationTest(hooks); + setupMirage(hooks); + + const instances = { + scopes: { + global: null, + org: null, + }, + authMethod: null, + }; + const urls = { + orgScope: null, + authMethods: null, + authMethod: null, + }; + + hooks.beforeEach(function () { + authenticateSession({}); + instances.scopes.global = this.server.create('scope', { id: 'global' }); + instances.scopes.org = this.server.create('scope', { + type: 'org', + scope: { id: 'global', type: 'global' }, + }); + instances.authMethod = this.server.create('auth-method', { + scope: instances.scopes.org, + type: 'oidc', + }); + + urls.orgScope = `/scopes/${instances.scopes.org.id}`; + urls.authMethods = `${urls.orgScope}/auth-methods`; + urls.authMethod = `${urls.authMethods}/${instances.authMethod.id}`; + }); + + test('visiting oidc auth method', async function (assert) { + assert.expect(1); + await visit(urls.authMethod); + await a11yAudit(); + assert.equal(currentURL(), urls.authMethod); + }); + + test('can view oidc state', async function(assert) { + assert.expect(1); + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + assert.equal( + find('.rose-dropdown[open] input[aria-checked=true]').value, + instances.authMethod.attributes.state + ); + }); + + test('can update oidc state', async function(assert) { + assert.expect(1); + const updateValue = 'inactive'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('can update oidc state to active-private', async function(assert) { + assert.expect(1); + const updateValue = 'active-private'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('can update oidc state to active-public', async function(assert) { + assert.expect(1); + // Update default 'active-public' state to inactive + instances.authMethod.attributes.state = 'inactive'; + const updateValue = 'active-public'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + await click(`.rose-dropdown[open] input[value="${updateValue}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.equal(authMethod.attributes.state, updateValue); + }); + + test('errors are displayed when state update fails', async function (assert) { + assert.expect(2); + const newState = 'inactive'; + await visit(urls.authMethod); + await click('.rose-layout-page-actions .rose-dropdown-trigger'); + // Load page before mocking update + this.server.post('/auth-methods/:idMethod', () => { + return new Response( + 400, + {}, + { + status: 400, + code: 'error', + message: 'Sorry!', + } + ); + }); + await click(`.rose-dropdown[open] input[value="${newState}"]`); + const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + assert.notEqual( + newState, + authMethod.attributes.state, + 'Auth method state is not be updated.' + ); + assert.ok( + find('[role="alert"]').textContent.trim(), + 'Sorry!', + 'Displays error message.' + ); + }); +}); From a6672ecef4ccf18f94e7a67c8e4e5074046b54b7 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 2 Jun 2021 14:03:53 -0400 Subject: [PATCH 14/23] chore: Rebase onto feature-oidc-array-fields-crud branch --- addons/api/mirage/config.js | 32 ++------- addons/api/mirage/route-handlers/auth.js | 33 +++++++-- addons/api/mirage/scenarios/default.js | 6 -- ui/admin/app/styles/app.scss | 2 +- .../auth-methods/auth-method/-actions.hbs | 6 +- .../acceptance/auth-methods/oidc-test.js | 68 +++++++++---------- 6 files changed, 69 insertions(+), 78 deletions(-) diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index d34110910..007950f69 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import config from '../config/environment'; -import { deauthHandler } from './route-handlers/auth'; +import { authHandler, deauthHandler } from './route-handlers/auth'; import { pickRandomStatusString } from './factories/session'; import { Response } from 'miragejs'; import initializeMockIPC from './scenarios/ipc'; @@ -71,31 +71,6 @@ export default function () { } return new Response(202); }); - this.post('/auth-methods/:idMethod', function (schema, request) { - const idMethod = request.params.idMethod; - const id = idMethod.split(':')[0]; - const method = idMethod.split(':')[1]; - - if (method === 'authenticate') { - schema.authHandler.call(this, schema, request); - return; - } - - const attrs = this.normalizedRequestAttrs(); - const authMethods = schema.authMethods; - const authMethod = authMethods.find(id); - let updatedAttrs = {}; - - if (method === 'change-state') { - updatedAttrs = { - attributes: { - state: attrs.attributes.state, - }, - }; - } - - return authMethod.update(updatedAttrs); - }); // Auth Method Accounts this.get( @@ -131,8 +106,9 @@ export default function () { } ); - // Authenticate/deauthenticate routes - // this.post('/auth-methods/:id_method', authHandler); + // Authenticate + auth methods custom routes + this.post('/auth-methods/:id_method', authHandler); + // Deauthenticate route this.post('/scopes/:id_method', deauthHandler); // IAM : Users diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 522ac0b52..5bba5d92a 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -107,14 +107,35 @@ const commandHandlers = { }, }; +// Handles all custom methods on /auth-methods/:id route export function authHandler({ scopes, authMethods }, request) { - const payload = JSON.parse(request.requestBody); - const [, id] = request.params.idMethod.match(/(?.[^:]*)/); - const { command } = payload; + const [, id, method] = request.params.id_method.match( + /(?.[^:]*):(?(.*))/ + ); const authMethod = authMethods.find(id); - const scope = scopes.find(authMethod.scopeId); - const scopeAttrs = this.serialize(scopes.find(scope.id)); - return commandHandlers[authMethod.type][command](payload, scopeAttrs); + + if (method === 'authenticate') { + const payload = JSON.parse(request.requestBody); + const { command } = payload; + const scope = scopes.find(authMethod.scopeId); + const scopeAttrs = this.serialize(scopes.find(scope.id)); + return commandHandlers[authMethod.type][command](payload, scopeAttrs); + } + + if (method === 'change-state') { + const attrs = this.normalizedRequestAttrs(); + let updatedAttrs = {}; + + if (method === 'change-state') { + updatedAttrs = { + attributes: { + state: attrs.attributes.state, + }, + }; + } + + return authMethod.update(updatedAttrs); + } } export function deauthHandler() { diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index 1f2595bb8..62018788c 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -1,5 +1,3 @@ -import { authHandler } from '../route-handlers/auth'; - export default function (server) { // Scope resources @@ -52,8 +50,4 @@ export default function (server) { server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); }); - - // Authenticate route handler - // TODO: Better way to pass-in auth handler - server.schema.authHandler = authHandler; } diff --git a/ui/admin/app/styles/app.scss b/ui/admin/app/styles/app.scss index 9defaac7b..8ee952891 100644 --- a/ui/admin/app/styles/app.scss +++ b/ui/admin/app/styles/app.scss @@ -83,9 +83,9 @@ .rose-form-fieldset { .rose-table { .rose-form-label { + left: -9999px; position: absolute; top: -9999px; - left: -9999px; } } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 5b05d127a..5ff803bfb 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -1,7 +1,7 @@ {{#if @model.isOIDC}} {{t 'resources.auth-method.actions.delete'}} - + \ No newline at end of file diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js index 75b89edeb..4d35c2a9f 100644 --- a/ui/admin/tests/acceptance/auth-methods/oidc-test.js +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -59,7 +59,7 @@ module('Acceptance | auth methods | oidc', function (hooks) { assert.equal(currentURL(), urls.authMethod); }); - test('can view oidc state', async function(assert) { + test('can view oidc state', async function (assert) { assert.expect(1); await visit(urls.authMethod); await click('.rose-layout-page-actions .rose-dropdown-trigger'); @@ -69,7 +69,7 @@ module('Acceptance | auth methods | oidc', function (hooks) { ); }); - test('can update oidc state', async function(assert) { + test('can update oidc state', async function (assert) { assert.expect(1); const updateValue = 'inactive'; await visit(urls.authMethod); @@ -79,7 +79,7 @@ module('Acceptance | auth methods | oidc', function (hooks) { assert.equal(authMethod.attributes.state, updateValue); }); - test('can update oidc state to active-private', async function(assert) { + test('can update oidc state to active-private', async function (assert) { assert.expect(1); const updateValue = 'active-private'; await visit(urls.authMethod); @@ -89,7 +89,7 @@ module('Acceptance | auth methods | oidc', function (hooks) { assert.equal(authMethod.attributes.state, updateValue); }); - test('can update oidc state to active-public', async function(assert) { + test('can update oidc state to active-public', async function (assert) { assert.expect(1); // Update default 'active-public' state to inactive instances.authMethod.attributes.state = 'inactive'; @@ -101,34 +101,34 @@ module('Acceptance | auth methods | oidc', function (hooks) { assert.equal(authMethod.attributes.state, updateValue); }); - test('errors are displayed when state update fails', async function (assert) { - assert.expect(2); - const newState = 'inactive'; - await visit(urls.authMethod); - await click('.rose-layout-page-actions .rose-dropdown-trigger'); - // Load page before mocking update - this.server.post('/auth-methods/:idMethod', () => { - return new Response( - 400, - {}, - { - status: 400, - code: 'error', - message: 'Sorry!', - } - ); - }); - await click(`.rose-dropdown[open] input[value="${newState}"]`); - const authMethod = this.server.db.authMethods.find(instances.authMethod.id); - assert.notEqual( - newState, - authMethod.attributes.state, - 'Auth method state is not be updated.' - ); - assert.ok( - find('[role="alert"]').textContent.trim(), - 'Sorry!', - 'Displays error message.' - ); - }); + // test('errors are displayed when state update fails', async function (assert) { + // assert.expect(2); + // const newState = 'inactive'; + // await visit(urls.authMethod); + // await click('.rose-layout-page-actions .rose-dropdown-trigger'); + // // Load page before mocking update + // this.server.post('/auth-methods/:idMethod', () => { + // return new Response( + // 400, + // {}, + // { + // status: 400, + // code: 'error', + // message: 'Sorry!', + // } + // ); + // }); + // await click(`.rose-dropdown[open] input[value="${newState}"]`); + // const authMethod = this.server.db.authMethods.find(instances.authMethod.id); + // assert.notEqual( + // newState, + // authMethod.attributes.state, + // 'Auth method state is not be updated.' + // ); + // assert.ok( + // find('[role="alert"]').textContent.trim(), + // 'Sorry!', + // 'Displays error message.' + // ); + // }); }); From 4bd80a716e9e2c71bba8d182eb9c8d955a130b4c Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 2 Jun 2021 14:07:38 -0400 Subject: [PATCH 15/23] Revert "Merge branch 'ICU-1369-oidc-status-change' of github.com:hashicorp/boundary-ui into ICU-1369-oidc-status-change" This reverts commit 8098beb450b4b19634cf31cca82ba0355d80cf07, reversing changes made to a6672ecef4ccf18f94e7a67c8e4e5074046b54b7. --- addons/api/mirage/config.js | 27 +------------------ addons/api/mirage/scenarios/default.js | 6 ----- .../auth-methods/auth-method/-actions.hbs | 2 +- 3 files changed, 2 insertions(+), 33 deletions(-) diff --git a/addons/api/mirage/config.js b/addons/api/mirage/config.js index 40a582e2c..007950f69 100644 --- a/addons/api/mirage/config.js +++ b/addons/api/mirage/config.js @@ -1,6 +1,6 @@ import Ember from 'ember'; import config from '../config/environment'; -import { deauthHandler } from './route-handlers/auth'; +import { authHandler, deauthHandler } from './route-handlers/auth'; import { pickRandomStatusString } from './factories/session'; import { Response } from 'miragejs'; import initializeMockIPC from './scenarios/ipc'; @@ -71,31 +71,6 @@ export default function () { } return new Response(202); }); - this.post('/auth-methods/:idMethod', function (schema, request) { - const idMethod = request.params.idMethod; - const id = idMethod.split(':')[0]; - const method = idMethod.split(':')[1]; - - if (method === 'authenticate') { - schema.authHandler.call(this, schema, request); - return; - } - - const attrs = this.normalizedRequestAttrs(); - const authMethods = schema.authMethods; - const authMethod = authMethods.find(id); - let updatedAttrs = {}; - - if (method === 'change-state') { - updatedAttrs = { - attributes: { - state: attrs.attributes.state, - }, - }; - } - - return authMethod.update(updatedAttrs); - }); // Auth Method Accounts this.get( diff --git a/addons/api/mirage/scenarios/default.js b/addons/api/mirage/scenarios/default.js index 1f2595bb8..62018788c 100644 --- a/addons/api/mirage/scenarios/default.js +++ b/addons/api/mirage/scenarios/default.js @@ -1,5 +1,3 @@ -import { authHandler } from '../route-handlers/auth'; - export default function (server) { // Scope resources @@ -52,8 +50,4 @@ export default function (server) { server.createList('target', 2, { scope }, 'withRandomHostSets'); server.createList('session', 4, { scope }, 'withAssociations'); }); - - // Authenticate route handler - // TODO: Better way to pass-in auth handler - server.schema.authHandler = authHandler; } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 397823a8e..5ff803bfb 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -67,4 +67,4 @@ > {{t 'resources.auth-method.actions.delete'}} - + \ No newline at end of file From 66d16d84d0a9bdf8926448c86d44cd7fdfd9e90a Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 2 Jun 2021 14:23:54 -0400 Subject: [PATCH 16/23] =?UTF-8?q?fix:=20=F0=9F=90=9B=20use=20flattened=20a?= =?UTF-8?q?ttrs=20to=20capture=20oidc=20field=20errors?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/adapters/application.js | 6 +- addons/api/addon/models/auth-method.js | 21 +++ .../form/auth-method/oidc/index.hbs | 134 ++++++++---------- 3 files changed, 82 insertions(+), 79 deletions(-) diff --git a/addons/api/addon/adapters/application.js b/addons/api/addon/adapters/application.js index 279e9b73f..12112ed67 100644 --- a/addons/api/addon/adapters/application.js +++ b/addons/api/addon/adapters/application.js @@ -248,7 +248,11 @@ export default class ApplicationAdapter extends RESTAdapter.extend( // Normalize field-specific errors, if any. const errors = fieldErrors.map((error) => ({ detail: error.description, - source: { pointer: `/data/attributes/${error.name}` }, + // Ember Data cannot handle errors on nested attributes, so these are + // flattened (e.g. `attributes.foobar` becomes `attributes_foobar`). + // To capture these errors, add read-only fields on the model + // corresponding to the underscored field name. + source: { pointer: `/data/attributes/${error.name.replace('.', '_')}` }, })); // Return a list of JSON API errors rooted under the `errors` key. return { diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index dc03abc0d..66f34757d 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -1,4 +1,5 @@ import GeneratedAuthMethodModel from '../generated/models/auth-method'; +import { attr } from '@ember-data/model'; import { fragment } from 'ember-data-model-fragments/attributes'; import { equal } from '@ember/object/computed'; @@ -27,6 +28,26 @@ export const options = { }; export default class AuthMethodModel extends GeneratedAuthMethodModel { + // =error attributes + // These attributes exist solely to capture errors on nested fields. + // See the application adapter's error normalization method for + // more information. + + @attr('string', { readOnly: true }) attributes_state; + @attr('string', { readOnly: true }) attributes_issuer; + @attr('string', { readOnly: true }) attributes_client_id; + @attr('string', { readOnly: true }) attributes_client_secret; + @attr('string', { readOnly: true }) attributes_max_age; + @attr('string', { readOnly: true }) attributes_api_url_prefix; + @attr('string', { readOnly: true }) + attributes_disable_discovered_config_validation; + @attr('string', { readOnly: true }) attributes_dry_run; + @attr('string', { readOnly: true }) attributes_account_claim_maps; + @attr('string', { readOnly: true }) attributes_claims_scopes; + @attr('string', { readOnly: true }) attributes_signing_algorithms; + @attr('string', { readOnly: true }) attributes_allowed_audiences; + @attr('string', { readOnly: true }) attributes_idp_ca_certs; + // =attributes /** diff --git a/ui/admin/app/components/form/auth-method/oidc/index.hbs b/ui/admin/app/components/form/auth-method/oidc/index.hbs index 335392e08..e1a5592c0 100644 --- a/ui/admin/app/components/form/auth-method/oidc/index.hbs +++ b/ui/admin/app/components/form/auth-method/oidc/index.hbs @@ -57,13 +57,13 @@ @value={{@model.attributes.issuer}} @label={{t 'form.issuer.label'}} @helperText={{t 'form.issuer.help'}} - @error={{@model.errors.attributes.issuer}} + @error={{@model.errors.attributes_issuer}} readonly={{false}} as |field| > - {{#if @model.errors.attributes.issuer}} + {{#if @model.errors.attributes_issuer}} - {{#each @model.errors.attributes.issuer as |error|}} + {{#each @model.errors.attributes_issuer as |error|}} {{error.message}} {{/each}} @@ -86,13 +86,13 @@ @value={{@model.attributes.client_id}} @label={{t 'form.client_id.label'}} @helperText={{t 'form.client_id.help'}} - @error={{@model.errors.attributes.client_id}} + @error={{@model.errors.attributes_client_id}} readonly={{false}} as |field| > - {{#if @model.errors.attributes.client_id}} + {{#if @model.errors.attributes_client_id}} - {{#each @model.errors.attributes.client_id as |error|}} + {{#each @model.errors.attributes_client_id as |error|}} {{error.message}} {{/each}} @@ -105,13 +105,13 @@ @value={{@model.attributes.client_secret}} @label={{t 'form.client_secret.label'}} @helperText={{t 'form.client_secret.help'}} - @error={{@model.errors.attributes.client_secret}} + @error={{@model.errors.attributes_client_secret}} readonly={{false}} as |field| > - {{#if @model.errors.attributes.client_secret}} + {{#if @model.errors.attributes_client_secret}} - {{#each @model.errors.attributes.client_secret as |error|}} + {{#each @model.errors.attributes_client_secret as |error|}} {{error.message}} {{/each}} @@ -123,6 +123,13 @@ <:title>{{t 'form.signing_algorithms.label'}} <:description>{{t 'form.signing_algorithms.help'}} <:body> + {{#if @model.errors.attributes_signing_algorithms}} + + {{#each @model.errors.attributes_signing_algorithms as |error|}} + {{error.message}} + {{/each}} + + {{/if}} @@ -136,7 +143,7 @@ {{algorithm}} {{/each}} - {{#if @model.errors.attributes.signing_algorithms}} - - {{#each - @model.errors.attributes.signing_algorithms - as |error| - }} - {{error.message}} - {{/each}} - - {{/if}} @@ -225,6 +222,13 @@ <:title>{{t 'form.allowed_audiences.label'}} <:description>{{t 'form.allowed_audiences.help'}} <:body> + {{#if @model.errors.attributes_allowed_audiences}} + + {{#each @model.errors.attributes_allowed_audiences as |error|}} + {{error.message}} + {{/each}} + + {{/if}} @@ -239,22 +243,12 @@ {{t 'form.claim.label'}} - {{#if @model.errors.attributes.allowed_audiences}} - - {{#each - @model.errors.attributes.allowed_audiences - as |error| - }} - {{error.message}} - {{/each}} - - {{/if}} @@ -310,6 +304,13 @@ <:title>{{t 'form.claims_scopes.label'}} <:description>{{t 'form.claims_scopes.help'}} <:body> + {{#if @model.errors.attributes_claims_scopes}} + + {{#each @model.errors.attributes_claims_scopes as |error|}} + {{error.message}} + {{/each}} + + {{/if}} @@ -324,22 +325,12 @@ {{t 'form.claims_scope.label'}} - {{#if @model.errors.attributes.claims_scopes}} - - {{#each - @model.errors.attributes.claims_scopes - as |error| - }} - {{error.message}} - {{/each}} - - {{/if}} @@ -395,6 +386,13 @@ <:title>{{t 'form.account_claim_maps.label'}} <:description>{{t 'form.account_claim_maps.help'}} <:body> + {{#if @model.errors.attributes_account_claim_maps}} + + {{#each @model.errors.attributes_account_claim_maps as |error|}} + {{error.message}} + {{/each}} + + {{/if}} @@ -410,28 +408,18 @@ {{t 'form.from_claim.label'}} - {{#if @model.errors.attributes.account_claim_maps}} - - {{#each - @model.errors.attributes.account_claim_maps - as |error| - }} - {{error.message}} - {{/each}} - - {{/if}} {{toClaim}} {{/each}} - {{#if @model.errors.attributes.account_claim_maps}} - - {{#each - @model.errors.attributes.account_claim_maps - as |error| - }} - {{error.message}} - {{/each}} - - {{/if}} @@ -521,6 +499,13 @@ <:title>{{t 'form.idp_ca_certs.label'}} <:description>{{t 'form.idp_ca_certs.help'}} <:body> + {{#if @model.errors.attributes_idp_ca_certs}} + + {{#each @model.errors.attributes_idp_ca_certs as |error|}} + {{error.message}} + {{/each}} + + {{/if}} @@ -534,19 +519,12 @@ {{t 'form.certificate.label'}} - {{#if @model.errors.attributes.idp_ca_certs}} - - {{#each @model.errors.attributes.idp_ca_certs as |error|}} - {{error.message}} - {{/each}} - - {{/if}} @@ -600,15 +578,15 @@ @value={{@model.attributes.max_age}} @label={{t 'form.max_age.label'}} @helperText={{t 'form.max_age.help'}} - @error={{@model.errors.attributes.max_age}} + @error={{@model.errors.attributes_max_age}} readonly={{false}} step='1' min='0' as |field| > - {{#if @model.errors.attributes.max_age}} + {{#if @model.errors.attributes_max_age}} - {{#each @model.errors.attributes.max_age as |error|}} + {{#each @model.errors.attributes_max_age as |error|}} {{error.message}} {{/each}} @@ -621,13 +599,13 @@ @value={{@model.attributes.api_url_prefix}} @label={{t 'form.api_url_prefix.label'}} @helperText={{t 'form.api_url_prefix.help'}} - @error={{@model.errors.attributes.api_url_prefix}} + @error={{@model.errors.attributes_api_url_prefix}} readonly={{false}} as |field| > - {{#if @model.errors.attributes.api_url_prefix}} + {{#if @model.errors.attributes_api_url_prefix}} - {{#each @model.errors.attributes.api_url_prefix as |error|}} + {{#each @model.errors.attributes_api_url_prefix as |error|}} {{error.message}} {{/each}} From 269c56bf83591a29f2f46797d46388321619da3f Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Wed, 2 Jun 2021 14:33:31 -0400 Subject: [PATCH 17/23] fix: lint issue --- ui/admin/tests/acceptance/auth-methods/oidc-test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js index 4d35c2a9f..bc53aa846 100644 --- a/ui/admin/tests/acceptance/auth-methods/oidc-test.js +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -10,7 +10,7 @@ import { import { setupApplicationTest } from 'ember-qunit'; import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; import a11yAudit from 'ember-a11y-testing/test-support/audit'; -import { Response } from 'miragejs'; +//import { Response } from 'miragejs'; import { authenticateSession, // These are left here intentionally for future reference. From a7a6bdedf9b1b53ffbb334f23d003525af6952d6 Mon Sep 17 00:00:00 2001 From: Susmitha Girumala Date: Wed, 2 Jun 2021 22:22:02 -0400 Subject: [PATCH 18/23] refactor: API: Add AuthMethodOIDC class to hold static states --- addons/api/addon/classes/auth-method.js | 15 +++++++++++++ addons/api/addon/models/auth-method.js | 2 -- addons/api/app/classes/auth-method.js | 1 + .../tests/unit/classes/auth-method-test.js | 22 +++++++++++++++++++ addons/core/translations/resources/en-us.yaml | 2 +- .../scopes/scope/auth-methods/auth-method.js | 9 ++++++++ .../scopes/scope/auth-methods/auth-method.js | 2 +- .../auth-methods/auth-method/-actions.hbs | 6 ++--- 8 files changed, 52 insertions(+), 7 deletions(-) create mode 100644 addons/api/addon/classes/auth-method.js create mode 100644 addons/api/app/classes/auth-method.js create mode 100644 addons/api/tests/unit/classes/auth-method-test.js diff --git a/addons/api/addon/classes/auth-method.js b/addons/api/addon/classes/auth-method.js new file mode 100644 index 000000000..799afb353 --- /dev/null +++ b/addons/api/addon/classes/auth-method.js @@ -0,0 +1,15 @@ +/** + * Empty auth method definition + */ +export class AuthMethod {} + +/** + * Simple OIDC auth method definition to contain possible oidc states. + * @property states {[string]} states + */ +export class AuthMethodOIDC extends AuthMethod { + /** + * Possible auth-method statuses + */ + static states = ['inactive', 'active-private', 'active-public']; +} diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index 1b2cf8182..09b801e0a 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -28,8 +28,6 @@ export const options = { }; export default class AuthMethodModel extends GeneratedAuthMethodModel { - STATES_ENUM = options.oidc.state; - // =error attributes // These attributes exist solely to capture errors on nested fields. // See the application adapter's error normalization method for diff --git a/addons/api/app/classes/auth-method.js b/addons/api/app/classes/auth-method.js new file mode 100644 index 000000000..5cdd5e1b1 --- /dev/null +++ b/addons/api/app/classes/auth-method.js @@ -0,0 +1 @@ +export { default } from 'api/classes/auth-method'; diff --git a/addons/api/tests/unit/classes/auth-method-test.js b/addons/api/tests/unit/classes/auth-method-test.js new file mode 100644 index 000000000..d2fb5d8c6 --- /dev/null +++ b/addons/api/tests/unit/classes/auth-method-test.js @@ -0,0 +1,22 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; +import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; +import { AuthMethod, AuthMethodOIDC } from 'api/classes/auth-method'; + +module('Unit | Class | auth method', function (hooks) { + setupTest(hooks); + setupMirage(hooks); + + test('it exists', function (assert) { + assert.ok(AuthMethod); + assert.ok(AuthMethodOIDC); + }); + + test('oidc auth method lists states', function (assert) { + assert.deepEqual(AuthMethodOIDC.states, [ + 'inactive', + 'active-private', + 'active-public', + ]); + }); +}); diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 0cab323c1..0c0eedb57 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -45,7 +45,7 @@ auth-method: delete: Delete Auth Method make-primary: Make Primary remove-as-primary: Remove as Primary - change-state: State + status: Status notifications: make-primary-success: Auth method successfully elected to primary. remove-as-primary-success: Auth method successfully removed as primary. diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js index 1005f0216..a095f6d40 100644 --- a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js @@ -1,4 +1,5 @@ import Controller from '@ember/controller'; +import { AuthMethodOIDC } from 'api/classes/auth-method'; export default class ScopesScopeAuthMethodsAuthMethodController extends Controller { // =attributes @@ -10,4 +11,12 @@ export default class ScopesScopeAuthMethodsAuthMethodController extends Controll get breadCrumb() { return this.model.displayName; } + + /** + * OIDC auth-method states + * @type {[string]} + */ + get oidcStates() { + return AuthMethodOIDC.states; + } } diff --git a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js index c492fed7b..c0678ed0a 100644 --- a/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/routes/scopes/scope/auth-methods/auth-method.js @@ -43,7 +43,7 @@ export default class ScopesScopeAuthMethodsAuthMethodRoute extends Route { /** * Update state of OIDC auth method - * @param {string} state - "inactive", "active-private", "active-public" + * @param {string} state */ @action @notifyError(({ message }) => message) diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 5ff803bfb..0a7476f53 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -1,14 +1,14 @@ {{#if @model.isOIDC}} - + - {{#each @model.STATES_ENUM as |state|}} + {{#each this.oidcStates as |state|}} Date: Wed, 2 Jun 2021 22:44:40 -0400 Subject: [PATCH 19/23] chore: Add in-progress oidc test for visibility --- .../acceptance/auth-methods/oidc-test.js | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/ui/admin/tests/acceptance/auth-methods/oidc-test.js b/ui/admin/tests/acceptance/auth-methods/oidc-test.js index bc53aa846..3bc1f226b 100644 --- a/ui/admin/tests/acceptance/auth-methods/oidc-test.js +++ b/ui/admin/tests/acceptance/auth-methods/oidc-test.js @@ -101,24 +101,29 @@ module('Acceptance | auth methods | oidc', function (hooks) { assert.equal(authMethod.attributes.state, updateValue); }); + // FIXME: How to mock just one request routed to /auth-methods and let everything else passthrough? // test('errors are displayed when state update fails', async function (assert) { // assert.expect(2); + // this.server.post('/auth-methods/:idMethod', (_, request) => { + // // Only respond to state update with error + // if (request.params.idMethod.match(/(change-state)/i)) { + // return new Response( + // 400, + // {}, + // { + // status: 400, + // code: 'error', + // message: 'Sorry!', + // } + // ); + // } + // return request; + // }); // const newState = 'inactive'; // await visit(urls.authMethod); // await click('.rose-layout-page-actions .rose-dropdown-trigger'); - // // Load page before mocking update - // this.server.post('/auth-methods/:idMethod', () => { - // return new Response( - // 400, - // {}, - // { - // status: 400, - // code: 'error', - // message: 'Sorry!', - // } - // ); - // }); // await click(`.rose-dropdown[open] input[value="${newState}"]`); + // // debugger; // const authMethod = this.server.db.authMethods.find(instances.authMethod.id); // assert.notEqual( // newState, From d0eb41e03b84fa0dabd69bb7ea269c423969b986 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Thu, 3 Jun 2021 17:08:40 -0400 Subject: [PATCH 20/23] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20icons=20and?= =?UTF-8?q?=20clean=20up=20state=20change=20language?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/classes/auth-method.js | 15 -------- addons/api/addon/models/auth-method.js | 21 +++++++++++ addons/api/app/classes/auth-method.js | 1 - .../tests/unit/classes/auth-method-test.js | 22 ------------ addons/core/translations/actions/en-us.yaml | 1 + addons/core/translations/resources/en-us.yaml | 1 - addons/core/translations/states/en-us.yaml | 9 ++--- addons/rose/package.json | 2 +- .../scopes/scope/auth-methods/auth-method.js | 4 +-- .../auth-methods/auth-method/-actions.hbs | 15 ++++---- yarn.lock | 35 +++++-------------- 11 files changed, 45 insertions(+), 81 deletions(-) delete mode 100644 addons/api/addon/classes/auth-method.js delete mode 100644 addons/api/app/classes/auth-method.js delete mode 100644 addons/api/tests/unit/classes/auth-method-test.js diff --git a/addons/api/addon/classes/auth-method.js b/addons/api/addon/classes/auth-method.js deleted file mode 100644 index 799afb353..000000000 --- a/addons/api/addon/classes/auth-method.js +++ /dev/null @@ -1,15 +0,0 @@ -/** - * Empty auth method definition - */ -export class AuthMethod {} - -/** - * Simple OIDC auth method definition to contain possible oidc states. - * @property states {[string]} states - */ -export class AuthMethodOIDC extends AuthMethod { - /** - * Possible auth-method statuses - */ - static states = ['inactive', 'active-private', 'active-public']; -} diff --git a/addons/api/addon/models/auth-method.js b/addons/api/addon/models/auth-method.js index 09b801e0a..8e6c4f198 100644 --- a/addons/api/addon/models/auth-method.js +++ b/addons/api/addon/models/auth-method.js @@ -75,6 +75,27 @@ export default class AuthMethodModel extends GeneratedAuthMethodModel { return this.type === 'oidc'; } + /** + * @type {boolean} + */ + get isInactive() { + return this.attributes.state === 'inactive'; + } + + /** + * @type {boolean} + */ + get isPrivate() { + return this.attributes.state === 'active-private'; + } + + /** + * @type {boolean} + */ + get isPublic() { + return this.attributes.state === 'active-public'; + } + // =methods /** diff --git a/addons/api/app/classes/auth-method.js b/addons/api/app/classes/auth-method.js deleted file mode 100644 index 5cdd5e1b1..000000000 --- a/addons/api/app/classes/auth-method.js +++ /dev/null @@ -1 +0,0 @@ -export { default } from 'api/classes/auth-method'; diff --git a/addons/api/tests/unit/classes/auth-method-test.js b/addons/api/tests/unit/classes/auth-method-test.js deleted file mode 100644 index d2fb5d8c6..000000000 --- a/addons/api/tests/unit/classes/auth-method-test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { module, test } from 'qunit'; -import { setupTest } from 'ember-qunit'; -import setupMirage from 'ember-cli-mirage/test-support/setup-mirage'; -import { AuthMethod, AuthMethodOIDC } from 'api/classes/auth-method'; - -module('Unit | Class | auth method', function (hooks) { - setupTest(hooks); - setupMirage(hooks); - - test('it exists', function (assert) { - assert.ok(AuthMethod); - assert.ok(AuthMethodOIDC); - }); - - test('oidc auth method lists states', function (assert) { - assert.deepEqual(AuthMethodOIDC.states, [ - 'inactive', - 'active-private', - 'active-public', - ]); - }); -}); diff --git a/addons/core/translations/actions/en-us.yaml b/addons/core/translations/actions/en-us.yaml index eccbad6eb..2c501df04 100644 --- a/addons/core/translations/actions/en-us.yaml +++ b/addons/core/translations/actions/en-us.yaml @@ -31,3 +31,4 @@ set-password: Set Password change-password: Change Password edit-form: Edit Form change-origin: Change Origin URL +change-state: Change State diff --git a/addons/core/translations/resources/en-us.yaml b/addons/core/translations/resources/en-us.yaml index 0c0eedb57..8ab5152ac 100644 --- a/addons/core/translations/resources/en-us.yaml +++ b/addons/core/translations/resources/en-us.yaml @@ -45,7 +45,6 @@ auth-method: delete: Delete Auth Method make-primary: Make Primary remove-as-primary: Remove as Primary - status: Status notifications: make-primary-success: Auth method successfully elected to primary. remove-as-primary-success: Auth method successfully removed as primary. diff --git a/addons/core/translations/states/en-us.yaml b/addons/core/translations/states/en-us.yaml index 23a92c6cf..aca77b801 100644 --- a/addons/core/translations/states/en-us.yaml +++ b/addons/core/translations/states/en-us.yaml @@ -8,9 +8,6 @@ connection: success: description: "Local proxy address ({protocol}): {address}:{port}" primary: Primary -auth-method: - oidc: - states: - inactive: Inactive - active-private: Active Private - active-public: Active Public +inactive: Inactive +active-private: Private +active-public: Public diff --git a/addons/rose/package.json b/addons/rose/package.json index aa389d5a0..2226f0281 100644 --- a/addons/rose/package.json +++ b/addons/rose/package.json @@ -43,7 +43,7 @@ "@ember/test-helpers": "^2.1.4", "@glimmer/component": "^1.0.3", "@glimmer/tracking": "^1.0.3", - "@hashicorp/structure-icons": "^1.8.1", + "@hashicorp/structure-icons": "^1.9.0-0", "@storybook/addon-actions": "^6.0.26", "@storybook/addon-docs": "^6.0.26", "@storybook/addon-knobs": "^6.0.26", diff --git a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js index a095f6d40..1aa269744 100644 --- a/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js +++ b/ui/admin/app/controllers/scopes/scope/auth-methods/auth-method.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -import { AuthMethodOIDC } from 'api/classes/auth-method'; +import { options } from 'api/models/auth-method'; export default class ScopesScopeAuthMethodsAuthMethodController extends Controller { // =attributes @@ -17,6 +17,6 @@ export default class ScopesScopeAuthMethodsAuthMethodController extends Controll * @type {[string]} */ get oidcStates() { - return AuthMethodOIDC.states; + return options.oidc.state; } } diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 0a7476f53..612a5e408 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -1,14 +1,15 @@ {{#if @model.isOIDC}} - + diff --git a/yarn.lock b/yarn.lock index 5ec5ecb54..f2e14d85f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2023,10 +2023,10 @@ resolved "https://registry.yarnpkg.com/@handlebars/parser/-/parser-1.1.0.tgz#d6dbc7574774b238114582410e8fee0dc3532bdf" integrity sha512-rR7tJoSwJ2eooOpYGxGGW95sLq6GXUaS1UtWvN7pei6n2/okYvCGld9vsUTvkl2migxbkszsycwtMf/GEc1k1A== -"@hashicorp/structure-icons@^1.8.1": - version "1.8.1" - resolved "https://registry.yarnpkg.com/@hashicorp/structure-icons/-/structure-icons-1.8.1.tgz#d29945df2b41dcb317b141e51a26bd4be796a164" - integrity sha512-XFYdCIshmaR3Igc8eWpOZ2Gr3IR/0TogXZ4PQ9bz1E9cLzF3njBcs3tCpJUOwRwe/wMI5YTlL/sOGvcZ77AB/Q== +"@hashicorp/structure-icons@^1.9.0-0": + version "1.9.0-0" + resolved "https://registry.yarnpkg.com/@hashicorp/structure-icons/-/structure-icons-1.9.0-0.tgz#7bd2f4a61d2dd81247090d2cfb86f1f89d4ea22a" + integrity sha512-khvUS/zThfR6pLW4hGVRpcGY2Ta5azeS/Vzb9iCfr3A8OhSZFdnrlkqjYknpBg4RwrQDBpWTzZhe/Z35Ne4Wxg== "@iarna/cli@^1.2.0": version "1.2.0" @@ -7560,7 +7560,7 @@ debug@~4.1.0: dependencies: ms "^2.1.1" -debuglog@*, debuglog@^1.0.1: +debuglog@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/debuglog/-/debuglog-1.0.1.tgz#aa24ffb9ac3df9a2351837cfb2d279360cd78492" integrity sha1-qiT/uaw9+aI1GDfPstJ5NgzXhJI= @@ -12369,7 +12369,7 @@ import-lazy@^4.0.0: resolved "https://registry.yarnpkg.com/import-lazy/-/import-lazy-4.0.0.tgz#e8eb627483a0a43da3c03f3e35548be5cb0cc153" integrity sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw== -imurmurhash@*, imurmurhash@^0.1.4: +imurmurhash@^0.1.4: version "0.1.4" resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" integrity sha1-khi5srkoojixPcT7a21XbyMUU+o= @@ -14067,11 +14067,6 @@ lodash._baseflatten@^3.0.0: lodash.isarguments "^3.0.0" lodash.isarray "^3.0.0" -lodash._baseindexof@*: - version "3.1.0" - resolved "https://registry.yarnpkg.com/lodash._baseindexof/-/lodash._baseindexof-3.1.0.tgz#fe52b53a1c6761e42618d654e4a25789ed61822c" - integrity sha1-/lK1OhxnYeQmGNZU5KJXie1hgiw= - lodash._baseuniq@~4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash._baseuniq/-/lodash._baseuniq-4.6.0.tgz#0ebb44e456814af7905c6212fa2c9b2d51b841e8" @@ -14080,16 +14075,11 @@ lodash._baseuniq@~4.6.0: lodash._createset "~4.0.0" lodash._root "~3.0.0" -lodash._bindcallback@*, lodash._bindcallback@^3.0.0: +lodash._bindcallback@^3.0.0: version "3.0.1" resolved "https://registry.yarnpkg.com/lodash._bindcallback/-/lodash._bindcallback-3.0.1.tgz#e531c27644cf8b57a99e17ed95b35c748789392e" integrity sha1-5THCdkTPi1epnhftlbNcdIeJOS4= -lodash._cacheindexof@*: - version "3.0.2" - resolved "https://registry.yarnpkg.com/lodash._cacheindexof/-/lodash._cacheindexof-3.0.2.tgz#3dc69ac82498d2ee5e3ce56091bafd2adc7bde92" - integrity sha1-PcaayCSY0u5ePOVgkbr9Ktx73pI= - lodash._createassigner@^3.0.0: version "3.1.1" resolved "https://registry.yarnpkg.com/lodash._createassigner/-/lodash._createassigner-3.1.1.tgz#838a5bae2fdaca63ac22dee8e19fa4e6d6970b11" @@ -14099,19 +14089,12 @@ lodash._createassigner@^3.0.0: lodash._isiterateecall "^3.0.0" lodash.restparam "^3.0.0" -lodash._createcache@*: - version "3.1.2" - resolved "https://registry.yarnpkg.com/lodash._createcache/-/lodash._createcache-3.1.2.tgz#56d6a064017625e79ebca6b8018e17440bdcf093" - integrity sha1-VtagZAF2JeeevKa4AY4XRAvc8JM= - dependencies: - lodash._getnative "^3.0.0" - lodash._createset@~4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/lodash._createset/-/lodash._createset-4.0.3.tgz#0f4659fbb09d75194fa9e2b88a6644d363c9fe26" integrity sha1-D0ZZ+7CddRlPqeK4imZE02PJ/iY= -lodash._getnative@*, lodash._getnative@^3.0.0: +lodash._getnative@^3.0.0: version "3.9.1" resolved "https://registry.yarnpkg.com/lodash._getnative/-/lodash._getnative-3.9.1.tgz#570bc7dede46d61cdcde687d65d3eecbaa3aaff5" integrity sha1-VwvH3t5G1hzc3mh9ZdPuy6o6r/U= @@ -14329,7 +14312,7 @@ lodash.pick@^4.4.0: resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" integrity sha1-UvBWEP/53tQiYRRB7R/BI6AwAbM= -lodash.restparam@*, lodash.restparam@^3.0.0: +lodash.restparam@^3.0.0: version "3.6.1" resolved "https://registry.yarnpkg.com/lodash.restparam/-/lodash.restparam-3.6.1.tgz#936a4e309ef330a7645ed4145986c85ae5b20805" integrity sha1-k2pOMJ7zMKdkXtQUWYbIWuWyCAU= From bf8620d75c8d04718a35fa2de52d1a5e24816784 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 4 Jun 2021 10:11:07 -0400 Subject: [PATCH 21/23] =?UTF-8?q?chore:=20=F0=9F=A4=96=20clean=20up=20chan?= =?UTF-8?q?ge-state=20handler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/mirage/route-handlers/auth.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/addons/api/mirage/route-handlers/auth.js b/addons/api/mirage/route-handlers/auth.js index 5bba5d92a..6f9a51634 100644 --- a/addons/api/mirage/route-handlers/auth.js +++ b/addons/api/mirage/route-handlers/auth.js @@ -122,19 +122,18 @@ export function authHandler({ scopes, authMethods }, request) { return commandHandlers[authMethod.type][command](payload, scopeAttrs); } + // TODO: this handler doesn't really belong here, but we already route + // POST requests on existing auth methods to this route handler file under the + // assumption it would be for authentication. While authentication should + // still occur here, we should handle other custom methods in the + // mirage config and route here only for auth. if (method === 'change-state') { const attrs = this.normalizedRequestAttrs(); - let updatedAttrs = {}; - - if (method === 'change-state') { - updatedAttrs = { - attributes: { - state: attrs.attributes.state, - }, - }; - } - - return authMethod.update(updatedAttrs); + return authMethod.update({ + attributes: { + state: attrs.attributes.state, + }, + }); } } From 12588a17abc3878c6acf7ad1b9937beb4b48c3f4 Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 4 Jun 2021 13:38:23 -0400 Subject: [PATCH 22/23] =?UTF-8?q?refactor:=20=F0=9F=92=A1=20type-specific?= =?UTF-8?q?=20account=20serialization?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../models/fragment-account-attributes.js | 8 ++++++ addons/api/addon/serializers/account.js | 23 ++++++++++++++++ .../fragment-account-attributes.js | 26 +++++++++++++++++++ .../fragment-account-attributes.js | 1 + .../fragment-account-attributes-test.js | 23 ++++++++++++++++ 5 files changed, 81 insertions(+) create mode 100644 addons/api/addon/serializers/fragment-account-attributes.js create mode 100644 addons/api/app/serializers/fragment-account-attributes.js create mode 100644 addons/api/tests/unit/serializers/fragment-account-attributes-test.js diff --git a/addons/api/addon/models/fragment-account-attributes.js b/addons/api/addon/models/fragment-account-attributes.js index bb0371398..0297f4399 100644 --- a/addons/api/addon/models/fragment-account-attributes.js +++ b/addons/api/addon/models/fragment-account-attributes.js @@ -2,8 +2,16 @@ import Fragment from 'ember-data-model-fragments/fragment'; import { attr } from '@ember-data/model'; export default class FragmentAccountAttributesModel extends Fragment { + // =attributes (password) @attr('string', { + for: 'password', description: 'The account login name', }) login_name; + + // =attributes (OIDC) + @attr('string', { for: 'oidc' }) subject; + @attr('string', { for: 'oidc' }) issuer; + @attr('string', { for: 'oidc', readOnly: true }) email; + @attr('string', { for: 'oidc', readOnly: true }) full_name; } diff --git a/addons/api/addon/serializers/account.js b/addons/api/addon/serializers/account.js index dfbb7e580..939aceb70 100644 --- a/addons/api/addon/serializers/account.js +++ b/addons/api/addon/serializers/account.js @@ -20,13 +20,36 @@ export default class AccountSerializer extends ApplicationSerializer { * @return {object} */ serialize(snapshot) { + switch (snapshot.record.type) { + case 'password': + return this.serializePassword(...arguments); + } + } + + /** + * Password serialization omits `attributes` and handles password-related + * custom methods. + * @param {Snapshot} snapshot + * @return {object} + */ + serializePassword(snapshot) { const password = snapshot?.adapterOptions?.password; let serialized = super.serialize(...arguments); + // Only login_name is serialized into password attributes. + const { login_name } = serialized.attributes; + if (login_name) { + serialized.attributes = { login_name }; + } else { + serialized.attributes = {}; + } + // New record case if (password && snapshot?.record?.isNew) serialized.attributes.password = password; + // Set password custom method if (snapshot?.adapterOptions?.method === 'set-password') { serialized = this.serializeForSetPassword(snapshot, password); } + // Change password custom method if (snapshot?.adapterOptions?.method === 'change-password') { const { currentPassword, newPassword } = snapshot?.adapterOptions; serialized = this.serializeForChangePassword( diff --git a/addons/api/addon/serializers/fragment-account-attributes.js b/addons/api/addon/serializers/fragment-account-attributes.js new file mode 100644 index 000000000..a9dcb590c --- /dev/null +++ b/addons/api/addon/serializers/fragment-account-attributes.js @@ -0,0 +1,26 @@ +import JSONSerializer from '@ember-data/serializer/json'; + +export default class FragmentAccountAttributesSerializer extends JSONSerializer { + /** + * If an attribute is annotated as readOnly in the model, don't serialize it. + * Otherwise delegate to default attribute serializer. + * + * @override + * @method serializeAttribute + * @param {Snapshot} snapshot + * @param {Object} json + * @param {String} key + * @param {Object} attribute + */ + serializeAttribute(snapshot, json, key, attribute) { + const { type, options } = attribute; + let value = super.serializeAttribute(...arguments); + // Convert empty strings to null. + if (type === 'string' && json[key] === '') json[key] = null; + // Do not serialize read-only attributes. + if (options.readOnly) delete json[key]; + // Client secret cannot be unset. If it's falsy, it must be removed. + if (key === 'client_secret' && !json[key]) delete json[key]; + return value; + } +} diff --git a/addons/api/app/serializers/fragment-account-attributes.js b/addons/api/app/serializers/fragment-account-attributes.js new file mode 100644 index 000000000..b37aa8148 --- /dev/null +++ b/addons/api/app/serializers/fragment-account-attributes.js @@ -0,0 +1 @@ +export { default } from 'api/serializers/fragment-account-attributes'; diff --git a/addons/api/tests/unit/serializers/fragment-account-attributes-test.js b/addons/api/tests/unit/serializers/fragment-account-attributes-test.js new file mode 100644 index 000000000..3245f8b80 --- /dev/null +++ b/addons/api/tests/unit/serializers/fragment-account-attributes-test.js @@ -0,0 +1,23 @@ +import { module, test } from 'qunit'; +import { setupTest } from 'ember-qunit'; + +module('Unit | Serializer | fragment account attributes', 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('fragment-account-attributes'); + + assert.ok(serializer); + }); + + test('it serializes records', function (assert) { + let store = this.owner.lookup('service:store'); + let record = store.createRecord('fragment-account-attributes', {}); + + let serializedRecord = record.serialize(); + + assert.ok(serializedRecord); + }); +}); From 13df776c180712d063cc3757519421a84040045c Mon Sep 17 00:00:00 2001 From: Randall Morey Date: Fri, 4 Jun 2021 14:23:08 -0400 Subject: [PATCH 23/23] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20OIDC=20accou?= =?UTF-8?q?nt=20listing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- addons/api/addon/models/account.js | 5 + addons/core/translations/form/en-us.yaml | 6 + .../auth-methods/auth-method/-actions.hbs | 38 ++++-- .../auth-methods/auth-method/-navigation.hbs | 14 +- .../auth-method/accounts/index.hbs | 123 ++++++++++++------ 5 files changed, 122 insertions(+), 64 deletions(-) diff --git a/addons/api/addon/models/account.js b/addons/api/addon/models/account.js index 90ad55c42..e5f702541 100644 --- a/addons/api/addon/models/account.js +++ b/addons/api/addon/models/account.js @@ -17,6 +17,11 @@ export default class AccountModel extends GeneratedAccountModel { */ @equal('type', 'password') isPassword; + /** + * @type {boolean} + */ + @equal('type', 'oidc') isOIDC; + /** * Convenience for getting username in account. * @type {string} diff --git a/addons/core/translations/form/en-us.yaml b/addons/core/translations/form/en-us.yaml index 65277af78..56af46588 100644 --- a/addons/core/translations/form/en-us.yaml +++ b/addons/core/translations/form/en-us.yaml @@ -100,3 +100,9 @@ signing_algorithms: help: The signing algorithms allowed for the auth method. algorithm: label: Algorithm +subject: + label: Subject +email: + label: Email +full_name: + label: Full Name diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs index 612a5e408..1ae2b32b3 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/-actions.hbs @@ -37,14 +37,6 @@ as |dropdown| > {{#if (feature-flag 'primary-auth-method')}} - {{#unless @model.isPrimary}} - - {{t 'resources.auth-method.actions.make-primary'}} - - {{/unless}} {{#if @model.isPrimary}} {{t 'resources.auth-method.actions.remove-as-primary'}} + {{else}} + + {{t 'resources.auth-method.actions.make-primary'}} + {{/if}} {{/if}} - - {{t 'resources.account.actions.create'}} - + {{#if @model.isOIDC}} + {{#if (feature-flag 'oidc-account-crud')}} + + {{t 'resources.account.actions.create'}} + + {{/if}} + {{else}} + + {{t 'resources.account.actions.create'}} + + {{/if}} {{t 'titles.details'}} - {{#if @model.isOIDC}} - {{#if (feature-flag 'oidc-account-crud')}} - - {{t 'resources.account.title_plural'}} - - {{/if}} - {{else}} - - {{t 'resources.account.title_plural'}} - - {{/if}} + + {{t 'resources.account.title_plural'}} + \ No newline at end of file diff --git a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs index 850e78c93..dd16fcb1b 100644 --- a/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs +++ b/ui/admin/app/templates/scopes/scope/auth-methods/auth-method/accounts/index.hbs @@ -1,46 +1,91 @@ {{#if @model}} - - - - {{t 'form.name.label'}} - - {{#if @model.firstObject.isPassword}} - {{t 'form.login_name.label'}} - {{/if}} - - {{t 'form.id.label'}} - - - - {{#each @model as |account|}} - - - + {{! OIDC accounts display more fields }} + {{#if @model.firstObject.isOIDC}} + + + + {{t 'form.name.label'}} + {{t 'form.issuer.label'}} + {{t 'form.subject.label'}} + {{t 'form.email.label'}} + {{t 'form.full_name.label'}} + {{t 'form.id.label'}} + + + + {{#each @model as |account|}} + + {{account.displayName}} - -

{{account.description}}

+
+ + {{account.attributes.issuer}} + + + {{account.attributes.subject}} + + + {{account.attributes.email}} + + + {{account.attributes.full_name}} + + + + {{account.id}} + + +
+ {{/each}} +
+
+ {{else}} + + + + {{t 'form.name.label'}} + + {{#if @model.firstObject.isPassword}} + {{t 'form.login_name.label'}} + {{/if}} - - {{account.username}} - - - - {{account.id}} - - - - {{/each}} - - - + {{t 'form.id.label'}} +
+
+ + {{#each @model as |account|}} + + + + {{account.displayName}} + +

{{account.description}}

+
+ + {{account.username}} + + + + {{account.id}} + + +
+ {{/each}} +
+
+ {{/if}} {{else}}