Merge pull request #15085 from hashicorp/ui/feature/net-889-prepopulate-partition-sso-login

ui: NET-889 pre-populate partition SSO login
This commit is contained in:
Tyler Wendlandt 2022-10-21 09:07:04 -06:00 committed by GitHub
commit 2354c06a93
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 261 additions and 285 deletions

View File

@ -53,6 +53,7 @@
<authForm.Method @matches="sso"> <authForm.Method @matches="sso">
<OidcSelect <OidcSelect
@dc={{@dc.Name}} @dc={{@dc.Name}}
@partition={{@partition}}
@nspace={{@nspace}} @nspace={{@nspace}}
@disabled={{authForm.disabled}} @disabled={{authForm.disabled}}
@onchange={{authForm.submit}} @onchange={{authForm.submit}}

View File

@ -1,76 +1,54 @@
<StateChart <StateChart @src={{this.chart}} as |State Guard ChartAction dispatch state|>
@src={{this.chart}} {{#let
as |State Guard ChartAction dispatch state|> (hash State=State Guard=Guard Action=ChartAction dispatch=dispatch state=state)
{{#let as |chart|
}}
{{#let
(hash (hash
State=State reset=(action dispatch 'RESET')
Guard=Guard
Action=ChartAction
dispatch=dispatch
state=state
)
as |chart|}}
{{#let
(hash
reset=(action dispatch "RESET")
focus=this.focus focus=this.focus
disabled=(state-matches state "loading") disabled=(state-matches state 'loading')
error=(queue error=(queue
(action dispatch "ERROR") (action dispatch 'ERROR') (action (mut this.error) value='error.errors.firstObject')
(action (mut this.error) value="error.errors.firstObject")
) )
submit=(queue submit=(queue (action (mut this.value)) (action dispatch 'SUBMIT'))
(action (mut this.value))
(action dispatch "SUBMIT")
) )
) as |exported|
as |exported|}} }}
<Guard <Guard @name='hasValue' @cond={{this.hasValue}} />
@name="hasValue"
@cond={{this.hasValue}}
/>
{{!TODO: Call this reset or similar }} {{!TODO: Call this reset or similar }}
<chart.Action <chart.Action
@name="clearError" @name='clearError'
@exec={{queue (action (mut this.error) undefined) (action (mut this.secret) undefined)}} @exec={{queue (action (mut this.error) undefined) (action (mut this.secret) undefined)}}
/> />
<div <div class='auth-form' ...attributes>
class="auth-form" <StateChart
...attributes
>
<StateChart
@src={{this.tabsChart}} @src={{this.tabsChart}}
as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|> as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|
{{#if (can 'use SSO')}} >
{{#if (can 'use SSO')}}
<TabNav <TabNav
@items={{array @items={{array
(hash (hash label='Token' selected=(state-matches tabState 'token'))
label='Token' (hash label='SSO' selected=(state-matches tabState 'sso'))
selected=(state-matches tabState 'token')
)
(hash
label='SSO'
selected=(state-matches tabState 'sso')
)
}} }}
@onclick={{queue (action tabDispatch) (action dispatch "RESET")}} @onclick={{queue (action tabDispatch) (action dispatch 'RESET')}}
/> />
{{/if}} {{/if}}
<State @matches="error"> <State @matches='error'>
{{#if this.error.status}} {{#if this.error.status}}
<Notice <Notice @type='error' role='alert' as |notice|>
@type="error"
role="alert"
as |notice|>
<notice.Body> <notice.Body>
<p> <p>
{{#if this.value.Name}} {{#if this.value.Name}}
{{#if (eq this.error.status '403')}} {{#if (eq this.error.status '403')}}
<strong>Consul login failed</strong><br /> <strong>Consul login failed</strong><br />
We received a token from your OIDC provider but could not log in to Consul with it. We received a token from your OIDC provider but could not log in to Consul
with it.
{{else if (eq this.error.status '401')}} {{else if (eq this.error.status '401')}}
<strong>Could not log in to provider</strong><br /> <strong>Could not log in to provider</strong><br />
The OIDC provider has rejected this access token. Please have an administrator check your auth method configuration. The OIDC provider has rejected this access token. Please have an
administrator check your auth method configuration.
{{else if (eq this.error.status '499')}} {{else if (eq this.error.status '499')}}
<strong>SSO log in window closed</strong><br /> <strong>SSO log in window closed</strong><br />
The OIDC provider window was closed. Please try again. The OIDC provider window was closed. Please try again.
@ -95,13 +73,14 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
</Notice> </Notice>
{{/if}} {{/if}}
</State> </State>
<TabState @matches="token"> <TabState @matches='token'>
<form <form onsubmit={{action dispatch 'SUBMIT'}}>
onsubmit={{action dispatch "SUBMIT"}}
>
<fieldset> <fieldset>
<label <label
class={{concat "type-password" (if (and (state-matches state 'error') (not this.error.status)) ' has-error')}} class={{concat
'type-password'
(if (and (state-matches state 'error') (not this.error.status)) ' has-error')
}}
> >
<span>Log in with a token</span> <span>Log in with a token</span>
@ -110,55 +89,54 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
{{! turn them into text inputs during acceptance testing }} {{! turn them into text inputs during acceptance testing }}
<input <input
{{did-insert (set this 'input')}} {{did-insert (set this 'input')}}
disabled={{state-matches state "loading"}} disabled={{state-matches state 'loading'}}
type={{if (eq (env 'environment') 'testing') 'text' 'password'}} type={{if (eq (env 'environment') 'testing') 'text' 'password'}}
name="auth[SecretID]" name='auth[SecretID]'
placeholder="SecretID" placeholder='SecretID'
value={{this.secret}} value={{this.secret}}
oninput={{queue oninput={{queue
(action (mut this.secret) value="target.value") (action (mut this.secret) value='target.value')
(action (mut this.value) value="target.value") (action (mut this.value) value='target.value')
(action dispatch "TYPING") (action dispatch 'TYPING')
}} }}
/> />
<State @matches="error"> <State @matches='error'>
{{#if (not this.error.status)}} {{#if (not this.error.status)}}
<strong role="alert"> <strong role='alert'>
Please enter your secret Please enter your secret
</strong> </strong>
{{/if}} {{/if}}
</State> </State>
</label> </label>
</fieldset> </fieldset>
<Action <Action @type='submit' disabled={{state-matches state 'loading'}}>
@type="submit"
disabled={{state-matches state "loading"}}
>
Log in Log in
</Action> </Action>
</form> </form>
</TabState> </TabState>
{{yield (assign exported (hash Method=TabState))}} {{yield (assign exported (hash Method=TabState))}}
<em> <em>
Contact your administrator for login credentials. Contact your administrator for login credentials.
</em> </em>
</StateChart> </StateChart>
</div> </div>
<State @matches="loading"> <State @matches='loading'>
<TokenSource <TokenSource
@dc={{@dc}} @dc={{@dc}}
@nspace={{or this.value.Namespace @nspace}} @nspace={{or this.value.Namespace @nspace}}
@partition={{or this.value.Partition @partition}} @partition={{or this.value.Partition @partition}}
@type={{if this.value.Name 'oidc' 'secret'}} @type={{if this.value.Name 'oidc' 'secret'}}
@value={{this.value}} @value={{this.value}}
@onchange={{queue (action dispatch "RESET") @onsubmit}} @onchange={{queue (action dispatch 'RESET') @onsubmit}}
@onerror={{queue (action (mut this.error) value="error.errors.firstObject") (action dispatch "ERROR")}} @onerror={{queue
(action (mut this.error) value='error.errors.firstObject')
(action dispatch 'ERROR')
}}
/> />
</State> </State>
{{/let}} {{/let}}
{{/let}} {{/let}}
</StateChart> </StateChart>

View File

@ -1,38 +1,25 @@
<StateChart <StateChart @src={{chart}} as |State Guard ChartAction dispatch state|>
@src={{chart}} {{#let
as |State Guard ChartAction dispatch state|> (hash State=State Guard=Guard Action=ChartAction dispatch=dispatch state=state)
{{#let as |chart|
(hash
State=State
Guard=Guard
Action=ChartAction
dispatch=dispatch
state=state
)
as |chart|}}
<div
class="oidc-select"
...attributes
>
<State @notMatches="idle">
<DataSource
@src={{uri '/${partition}/${nspace}/${dc}/oidc/providers'
(hash
partition=this.partition
nspace=@nspace
dc=@dc
)
}} }}
@onchange={{queue (action (mut this.items) value="data") (fn dispatch "SUCCESS")}}
@onerror={{queue (fn dispatch "RESET") @onerror}} <div class='oidc-select' ...attributes>
<State @notMatches='idle'>
<DataSource
@src={{uri
'/${partition}/${nspace}/${dc}/oidc/providers'
(hash partition=this.partition nspace=@nspace dc=@dc)
}}
@onchange={{queue (action (mut this.items) value='data') (fn dispatch 'SUCCESS')}}
@onerror={{queue (fn dispatch 'RESET') @onerror}}
/> />
</State> </State>
<State @matches="loaded"> <State @matches='loaded'>
<Action <Action
{{on 'click' (queue (set this 'partition' '') (fn dispatch "RESET"))}} {{on 'click' (queue (set this 'partition' '') (fn dispatch 'RESET'))}}
class="reset" class='reset'
> >
Choose different Partition Choose different Partition
</Action> </Action>
@ -40,10 +27,11 @@ as |chart|}}
<StateChart <StateChart
@src={{state-chart 'validate'}} @src={{state-chart 'validate'}}
as |ignoredState ignoredGuard ignoredAction formDispatch state|> as |ignoredState ignoredGuard ignoredAction formDispatch state|
>
<TextInput <TextInput
@name="partition" @name='partition'
@label="Admin Partition" @label='Admin Partition'
@item={{this}} @item={{this}}
@validations={{hash @validations={{hash
partition=(array partition=(array
@ -53,20 +41,17 @@ as |chart|}}
) )
) )
}} }}
@placeholder="Enter your Partition" @placeholder='Enter your Partition'
@oninput={{action (mut this.partition) value="target.value"}} @oninput={{action (mut this.partition) value='target.value'}}
@chart={{hash @chart={{hash state=state dispatch=formDispatch}}
state=state
dispatch=formDispatch
}}
/> />
{{! this belongs to the outer StateChart but we need }} {{! this belongs to the outer StateChart but we need }}
{{! to understand validation state }} {{! to understand validation state }}
<State @matches="idle"> <State @matches='idle'>
<Action <Action
{{disabled (or (lt this.partition.length 1) (state-matches state "error"))}} {{disabled (or (lt this.partition.length 1) (state-matches state 'error'))}}
{{on "click" (fn dispatch "LOAD")}} {{on 'click' (fn dispatch 'LOAD')}}
> >
Choose provider Choose provider
</Action> </Action>
@ -74,11 +59,11 @@ as |chart|}}
</StateChart> </StateChart>
<State @matches="loading"> <State @matches='loading'>
<Progress aria-label="Loading" /> <Progress aria-label='Loading' />
</State> </State>
<State @matches="loaded"> <State @matches='loaded'>
{{#if (lt this.items.length 3)}} {{#if (lt this.items.length 3)}}
<ul> <ul>
@ -87,10 +72,12 @@ as |chart|}}
<Action <Action
class={{concat item.Kind '-oidc-provider'}} class={{concat item.Kind '-oidc-provider'}}
disabled={{@disabled}} disabled={{@disabled}}
@type="button" @type='button'
{{on 'click' (fn @onchange item)}} {{on 'click' (fn @onchange item)}}
> >
Continue with {{or item.DisplayName item.Name}}{{#if (not-eq item.Namespace 'default')}} ({{item.Namespace}}){{/if}} Continue with
{{or item.DisplayName item.Name}}{{#if (not-eq item.Namespace 'default')}}
({{item.Namespace}}){{/if}}
</Action> </Action>
</li> </li>
{{/each}} {{/each}}
@ -101,8 +88,8 @@ as |chart|}}
{{#let (or this.provider (object-at 0 this.items)) as |item|}} {{#let (or this.provider (object-at 0 this.items)) as |item|}}
<OptionInput <OptionInput
@label="SSO Provider" @label='SSO Provider'
@name="provider" @name='provider'
@item={{this}} @item={{this}}
@selected={{item}} @selected={{item}}
@items={{this.items}} @items={{this.items}}
@ -110,19 +97,15 @@ as |chart|}}
@disabled={{@disabled}} @disabled={{@disabled}}
> >
<:option as |option|> <:option as |option|>
<span <span class={{concat option.item.Kind '-oidc-provider'}}>
class={{concat option.item.Kind '-oidc-provider'}} {{or option.item.DisplayName option.item.Name}}{{#if
> (not-eq option.item.Namespace 'default')
{{or option.item.DisplayName option.item.Name}}{{#if (not-eq option.item.Namespace 'default')}} ({{option.item.Namespace}}){{/if}} }} ({{option.item.Namespace}}){{/if}}
</span> </span>
</:option> </:option>
</OptionInput> </OptionInput>
<Action <Action @type='button' {{disabled @disabled}} {{on 'click' (fn @onchange item)}}>
@type="button"
{{disabled @disabled}}
{{on 'click' (fn @onchange item)}}
>
Log in Log in
</Action> </Action>
@ -130,5 +113,5 @@ as |chart|}}
{{/if}} {{/if}}
</State> </State>
</div> </div>
{{/let}} {{/let}}
</StateChart> </StateChart>

View File

@ -4,9 +4,14 @@ import { tracked } from '@glimmer/tracking';
import chart from './chart.xstate'; import chart from './chart.xstate';
export default class OidcSelect extends Component { export default class OidcSelect extends Component {
@tracked partition = ''; @tracked partition = 'default';
constructor() { constructor() {
super(...arguments); super(...arguments);
this.chart = chart; this.chart = chart;
if (this.args.partition) {
this.partition = this.args.partition;
}
} }
} }

View File

@ -41,6 +41,7 @@ Feature: login
--- ---
And I click login on the navigation And I click login on the navigation
And I click "[data-test-tab=tab_sso] button" And I click "[data-test-tab=tab_sso] button"
Then the "[name='partition']" input should have the value "default"
And I type "partition" into "[name=partition]" And I type "partition" into "[name=partition]"
And I click ".oidc-select button" And I click ".oidc-select button"
Then a GET request was made to "/v1/internal/ui/oidc-auth-methods?dc=dc-1&ns=@namespace&partition=partition" Then a GET request was made to "/v1/internal/ui/oidc-auth-methods?dc=dc-1&ns=@namespace&partition=partition"

View File

@ -85,5 +85,13 @@ export default function (scenario, assert, pauseUntil, find, currentURL, clipboa
}) })
.then(['the title should be "$title"'], function (title) { .then(['the title should be "$title"'], function (title) {
assert.equal(document.title, title, `Expected the document.title to equal "${title}"`); assert.equal(document.title, title, `Expected the document.title to equal "${title}"`);
})
.then(['the "$selector" input should have the value "$value"'], function (selector, value) {
const $el = find(selector);
assert.equal(
$el.value,
value,
`Expected the input at ${selector} to have value ${value}, but it had ${$el.value}`
);
}); });
} }