Pre-populate partition on sso login
This commit is contained in:
parent
702b63d819
commit
f89fc309ff
|
@ -51,8 +51,10 @@
|
|||
/>
|
||||
{{#if (can "use SSO")}}
|
||||
<authForm.Method @matches="sso">
|
||||
{{log (concat "Partition Parent: " @partition)}}
|
||||
<OidcSelect
|
||||
@dc={{@dc.Name}}
|
||||
@partition={{@partition}}
|
||||
@nspace={{@nspace}}
|
||||
@disabled={{authForm.disabled}}
|
||||
@onchange={{authForm.submit}}
|
||||
|
|
|
@ -1,76 +1,54 @@
|
|||
<StateChart
|
||||
@src={{this.chart}}
|
||||
as |State Guard ChartAction dispatch state|>
|
||||
<StateChart @src={{this.chart}} as |State Guard ChartAction dispatch state|>
|
||||
{{#let
|
||||
(hash State=State Guard=Guard Action=ChartAction dispatch=dispatch state=state)
|
||||
as |chart|
|
||||
}}
|
||||
{{#let
|
||||
(hash
|
||||
State=State
|
||||
Guard=Guard
|
||||
Action=ChartAction
|
||||
dispatch=dispatch
|
||||
state=state
|
||||
)
|
||||
as |chart|}}
|
||||
{{#let
|
||||
(hash
|
||||
reset=(action dispatch "RESET")
|
||||
reset=(action dispatch 'RESET')
|
||||
focus=this.focus
|
||||
disabled=(state-matches state "loading")
|
||||
disabled=(state-matches state 'loading')
|
||||
error=(queue
|
||||
(action dispatch "ERROR")
|
||||
(action (mut this.error) value="error.errors.firstObject")
|
||||
(action dispatch 'ERROR') (action (mut this.error) value='error.errors.firstObject')
|
||||
)
|
||||
submit=(queue
|
||||
(action (mut this.value))
|
||||
(action dispatch "SUBMIT")
|
||||
submit=(queue (action (mut this.value)) (action dispatch 'SUBMIT'))
|
||||
)
|
||||
)
|
||||
as |exported|}}
|
||||
<Guard
|
||||
@name="hasValue"
|
||||
@cond={{this.hasValue}}
|
||||
/>
|
||||
as |exported|
|
||||
}}
|
||||
<Guard @name='hasValue' @cond={{this.hasValue}} />
|
||||
{{!TODO: Call this reset or similar }}
|
||||
<chart.Action
|
||||
@name="clearError"
|
||||
@name='clearError'
|
||||
@exec={{queue (action (mut this.error) undefined) (action (mut this.secret) undefined)}}
|
||||
/>
|
||||
<div
|
||||
class="auth-form"
|
||||
...attributes
|
||||
>
|
||||
<div class='auth-form' ...attributes>
|
||||
<StateChart
|
||||
@src={{this.tabsChart}}
|
||||
as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
|
||||
as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|
|
||||
>
|
||||
{{#if (can 'use SSO')}}
|
||||
<TabNav
|
||||
@items={{array
|
||||
(hash
|
||||
label='Token'
|
||||
selected=(state-matches tabState 'token')
|
||||
)
|
||||
(hash
|
||||
label='SSO'
|
||||
selected=(state-matches tabState 'sso')
|
||||
)
|
||||
(hash label='Token' 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}}
|
||||
<State @matches="error">
|
||||
<State @matches='error'>
|
||||
{{#if this.error.status}}
|
||||
<Notice
|
||||
@type="error"
|
||||
role="alert"
|
||||
as |notice|>
|
||||
<Notice @type='error' role='alert' as |notice|>
|
||||
<notice.Body>
|
||||
<p>
|
||||
{{#if this.value.Name}}
|
||||
{{#if (eq this.error.status '403')}}
|
||||
<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')}}
|
||||
<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')}}
|
||||
<strong>SSO log in window closed</strong><br />
|
||||
The OIDC provider window was closed. Please try again.
|
||||
|
@ -95,13 +73,14 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
|
|||
</Notice>
|
||||
{{/if}}
|
||||
</State>
|
||||
<TabState @matches="token">
|
||||
<form
|
||||
onsubmit={{action dispatch "SUBMIT"}}
|
||||
>
|
||||
<TabState @matches='token'>
|
||||
<form onsubmit={{action dispatch 'SUBMIT'}}>
|
||||
<fieldset>
|
||||
<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>
|
||||
|
||||
|
@ -110,30 +89,27 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
|
|||
{{! turn them into text inputs during acceptance testing }}
|
||||
<input
|
||||
{{did-insert (set this 'input')}}
|
||||
disabled={{state-matches state "loading"}}
|
||||
disabled={{state-matches state 'loading'}}
|
||||
type={{if (eq (env 'environment') 'testing') 'text' 'password'}}
|
||||
name="auth[SecretID]"
|
||||
placeholder="SecretID"
|
||||
name='auth[SecretID]'
|
||||
placeholder='SecretID'
|
||||
value={{this.secret}}
|
||||
oninput={{queue
|
||||
(action (mut this.secret) value="target.value")
|
||||
(action (mut this.value) value="target.value")
|
||||
(action dispatch "TYPING")
|
||||
(action (mut this.secret) value='target.value')
|
||||
(action (mut this.value) value='target.value')
|
||||
(action dispatch 'TYPING')
|
||||
}}
|
||||
/>
|
||||
<State @matches="error">
|
||||
<State @matches='error'>
|
||||
{{#if (not this.error.status)}}
|
||||
<strong role="alert">
|
||||
<strong role='alert'>
|
||||
Please enter your secret
|
||||
</strong>
|
||||
{{/if}}
|
||||
</State>
|
||||
</label>
|
||||
</fieldset>
|
||||
<Action
|
||||
@type="submit"
|
||||
disabled={{state-matches state "loading"}}
|
||||
>
|
||||
<Action @type='submit' disabled={{state-matches state 'loading'}}>
|
||||
Log in
|
||||
</Action>
|
||||
</form>
|
||||
|
@ -146,17 +122,19 @@ as |TabState IgnoredGuard IgnoredAction tabDispatch tabState|>
|
|||
</em>
|
||||
</StateChart>
|
||||
|
||||
|
||||
</div>
|
||||
<State @matches="loading">
|
||||
<State @matches='loading'>
|
||||
<TokenSource
|
||||
@dc={{@dc}}
|
||||
@nspace={{or this.value.Namespace @nspace}}
|
||||
@partition={{or this.value.Partition @partition}}
|
||||
@type={{if this.value.Name 'oidc' 'secret'}}
|
||||
@value={{this.value}}
|
||||
@onchange={{queue (action dispatch "RESET") @onsubmit}}
|
||||
@onerror={{queue (action (mut this.error) value="error.errors.firstObject") (action dispatch "ERROR")}}
|
||||
@onchange={{queue (action dispatch 'RESET') @onsubmit}}
|
||||
@onerror={{queue
|
||||
(action (mut this.error) value='error.errors.firstObject')
|
||||
(action dispatch 'ERROR')
|
||||
}}
|
||||
/>
|
||||
</State>
|
||||
{{/let}}
|
||||
|
|
|
@ -1,38 +1,25 @@
|
|||
<StateChart
|
||||
@src={{chart}}
|
||||
as |State Guard ChartAction dispatch state|>
|
||||
<StateChart @src={{chart}} as |State Guard ChartAction dispatch state|>
|
||||
{{#let
|
||||
(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
|
||||
)
|
||||
(hash State=State Guard=Guard Action=ChartAction dispatch=dispatch state=state)
|
||||
as |chart|
|
||||
}}
|
||||
@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 @matches="loaded">
|
||||
<State @matches='loaded'>
|
||||
<Action
|
||||
{{on 'click' (queue (set this 'partition' '') (fn dispatch "RESET"))}}
|
||||
class="reset"
|
||||
{{on 'click' (queue (set this 'partition' '') (fn dispatch 'RESET'))}}
|
||||
class='reset'
|
||||
>
|
||||
Choose different Partition
|
||||
</Action>
|
||||
|
@ -40,10 +27,11 @@ as |chart|}}
|
|||
|
||||
<StateChart
|
||||
@src={{state-chart 'validate'}}
|
||||
as |ignoredState ignoredGuard ignoredAction formDispatch state|>
|
||||
as |ignoredState ignoredGuard ignoredAction formDispatch state|
|
||||
>
|
||||
<TextInput
|
||||
@name="partition"
|
||||
@label="Admin Partition"
|
||||
@name='partition'
|
||||
@label='Admin Partition'
|
||||
@item={{this}}
|
||||
@validations={{hash
|
||||
partition=(array
|
||||
|
@ -53,20 +41,17 @@ as |chart|}}
|
|||
)
|
||||
)
|
||||
}}
|
||||
@placeholder="Enter your Partition"
|
||||
@oninput={{action (mut this.partition) value="target.value"}}
|
||||
@chart={{hash
|
||||
state=state
|
||||
dispatch=formDispatch
|
||||
}}
|
||||
@placeholder='Enter your Partition'
|
||||
@oninput={{action (mut this.partition) value='target.value'}}
|
||||
@chart={{hash state=state dispatch=formDispatch}}
|
||||
/>
|
||||
|
||||
{{! this belongs to the outer StateChart but we need }}
|
||||
{{! to understand validation state }}
|
||||
<State @matches="idle">
|
||||
<State @matches='idle'>
|
||||
<Action
|
||||
{{disabled (or (lt this.partition.length 1) (state-matches state "error"))}}
|
||||
{{on "click" (fn dispatch "LOAD")}}
|
||||
{{disabled (or (lt this.partition.length 1) (state-matches state 'error'))}}
|
||||
{{on 'click' (fn dispatch 'LOAD')}}
|
||||
>
|
||||
Choose provider
|
||||
</Action>
|
||||
|
@ -74,11 +59,11 @@ as |chart|}}
|
|||
|
||||
</StateChart>
|
||||
|
||||
<State @matches="loading">
|
||||
<Progress aria-label="Loading" />
|
||||
<State @matches='loading'>
|
||||
<Progress aria-label='Loading' />
|
||||
</State>
|
||||
|
||||
<State @matches="loaded">
|
||||
<State @matches='loaded'>
|
||||
{{#if (lt this.items.length 3)}}
|
||||
|
||||
<ul>
|
||||
|
@ -87,10 +72,12 @@ as |chart|}}
|
|||
<Action
|
||||
class={{concat item.Kind '-oidc-provider'}}
|
||||
disabled={{@disabled}}
|
||||
@type="button"
|
||||
@type='button'
|
||||
{{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>
|
||||
</li>
|
||||
{{/each}}
|
||||
|
@ -101,8 +88,8 @@ as |chart|}}
|
|||
{{#let (or this.provider (object-at 0 this.items)) as |item|}}
|
||||
|
||||
<OptionInput
|
||||
@label="SSO Provider"
|
||||
@name="provider"
|
||||
@label='SSO Provider'
|
||||
@name='provider'
|
||||
@item={{this}}
|
||||
@selected={{item}}
|
||||
@items={{this.items}}
|
||||
|
@ -110,19 +97,15 @@ as |chart|}}
|
|||
@disabled={{@disabled}}
|
||||
>
|
||||
<:option as |option|>
|
||||
<span
|
||||
class={{concat option.item.Kind '-oidc-provider'}}
|
||||
>
|
||||
{{or option.item.DisplayName option.item.Name}}{{#if (not-eq option.item.Namespace 'default')}} ({{option.item.Namespace}}){{/if}}
|
||||
<span class={{concat option.item.Kind '-oidc-provider'}}>
|
||||
{{or option.item.DisplayName option.item.Name}}{{#if
|
||||
(not-eq option.item.Namespace 'default')
|
||||
}} ({{option.item.Namespace}}){{/if}}
|
||||
</span>
|
||||
</:option>
|
||||
</OptionInput>
|
||||
|
||||
<Action
|
||||
@type="button"
|
||||
{{disabled @disabled}}
|
||||
{{on 'click' (fn @onchange item)}}
|
||||
>
|
||||
<Action @type='button' {{disabled @disabled}} {{on 'click' (fn @onchange item)}}>
|
||||
Log in
|
||||
</Action>
|
||||
|
||||
|
|
|
@ -4,9 +4,14 @@ import { tracked } from '@glimmer/tracking';
|
|||
import chart from './chart.xstate';
|
||||
|
||||
export default class OidcSelect extends Component {
|
||||
@tracked partition = '';
|
||||
@tracked partition = 'default';
|
||||
|
||||
constructor() {
|
||||
super(...arguments);
|
||||
this.chart = chart;
|
||||
|
||||
if (this.args.partition) {
|
||||
this.partition = this.args.partition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -41,6 +41,7 @@ Feature: login
|
|||
---
|
||||
And I click login on the navigation
|
||||
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 click ".oidc-select button"
|
||||
Then a GET request was made to "/v1/internal/ui/oidc-auth-methods?dc=dc-1&ns=@namespace&partition=partition"
|
||||
|
@ -49,3 +50,36 @@ Feature: login
|
|||
And a POST request was made to "/v1/acl/oidc/callback?dc=dc-1&ns=@!namespace&partition=partition"
|
||||
And "[data-notification]" has the "notification-authorize" class
|
||||
And "[data-notification]" has the "success" class
|
||||
Scenario: Logging in via SSO with a partition chosen
|
||||
Given 1 datacenter model with the value "dc-1"
|
||||
And SSO is enabled
|
||||
And partitions are enabled
|
||||
And 1 partition model with the value "_example-partition"
|
||||
And 1 oidcProvider model from yaml
|
||||
---
|
||||
- DisplayName: Okta
|
||||
Name: okta
|
||||
Kind: okta
|
||||
---
|
||||
When I visit the services page for yaml
|
||||
---
|
||||
dc: dc-1
|
||||
partition: example-partition
|
||||
---
|
||||
And the "okta" oidcProvider responds with from yaml
|
||||
---
|
||||
state: state-123456789/abcdefghijklmnopqrstuvwxyz
|
||||
code: code-abcdefghijklmnopqrstuvwxyz/123456789
|
||||
---
|
||||
And I click login on the navigation
|
||||
And I click "[data-test-tab=tab_sso] button"
|
||||
Then the "[name='partition']" input should have the value "example-partition"
|
||||
And I type "partition" into "[name=partition]"
|
||||
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"
|
||||
And I click ".okta-oidc-provider"
|
||||
Then a POST request was made to "/v1/acl/oidc/auth-url?dc=dc-1&ns=@!namespace&partition=partition"
|
||||
And a POST request was made to "/v1/acl/oidc/callback?dc=dc-1&ns=@!namespace&partition=partition"
|
||||
And "[data-notification]" has the "notification-authorize" class
|
||||
And "[data-notification]" has the "success" class
|
||||
|
||||
|
|
|
@ -66,6 +66,8 @@ export function visitable(path, encoder = encodeURIComponent) {
|
|||
let path = paths.shift();
|
||||
if (typeof dynamicSegmentsAndQueryParams.nspace !== 'undefined') {
|
||||
path = `/:nspace${path}`;
|
||||
} else if (typeof dynamicSegmentsAndQueryParams.partition !== 'undefined') {
|
||||
path = `/:partition${path}`;
|
||||
}
|
||||
params = assign({}, dynamicSegmentsAndQueryParams);
|
||||
let fullPath;
|
||||
|
|
|
@ -85,5 +85,13 @@ export default function (scenario, assert, pauseUntil, find, currentURL, clipboa
|
|||
})
|
||||
.then(['the title should be "$title"'], function (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}`
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue