diff --git a/ui/packages/consul-ui/app/components/jwt-source/README.mdx b/ui/packages/consul-ui/app/components/jwt-source/README.mdx index 53e442525..cd8c602d5 100644 --- a/ui/packages/consul-ui/app/components/jwt-source/README.mdx +++ b/ui/packages/consul-ui/app/components/jwt-source/README.mdx @@ -1,27 +1,93 @@ ---- -class: ember ---- # JwtSource -```hbs - +This is a Source-like component and with most of our Source components, the +component only needs to be added to the page in order to start the flow and is +meant to be used as such (think 'this is like an ``). + +The component will go through the steps of requesting a JWT token from a third +party OAuth provider. `src` should contain the full URL of the authorization +URL for the 3rd party provider including `redirect_uri`. Once the user has +logged into the 3rd party provider the `onchange` event will be fired +containing an event-like object whose data contains the JWT information. + +During development you can use the `CONSUL_OIDC_PROVIDER_URL` +environment/cookie value to inject/mock the provider URL of your choice. This +var/cookie value **does not** need the `redirect_uri` parameter adding as that +will be automatically added by the UI . This URL will then be returned by our +mock API when requesting the AuthURL for **all** OIDC AuthMethods. + +This component **does not store the resulting JWT**, it only emits it via +its `onchange` argument/event handler. Errors are emitted via the `onerror` +argument/event handler. + + +```hbs preview-template +
+
Provide a widget to set the URL
+ + + +
+ +
+
When the button is clicked add TokenSource to the page
+ +{{#if (eq this.state 'authenticating')}} + +{{/if}} +
+
+
Show the results
+
+{{#if this.jwt}} +
authorizationState
+
{{this.jwt.authorizationState}}
+
authorizationCode
+
{{this.jwt.authorizationCode}}
+
provider
+
{{this.jwt.provider}}
+{{/if}} +{{#if this.error}} +
Error
+
{{this.error.detail}}
+{{/if}} +
+
``` -### Arguments +## Arguments | Argument | Type | Default | Description | | --- | --- | --- | --- | -| `src` | `String` | | The source to subscribe to updates to, this should map to a string based URI | -| `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the jwt data, in this case the autorizationCode and the status | +| `src` | `String` | | The **full** AuthURL for the 3rd party OIDC provider as provided by Consul (including `redirect_uri`) | +| `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the JWT data, in this case the `autorizationStatus`, `autorizationCode`, plus the UI added `provider` name | | `onerror` | `Function` | | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. | -This component will go through the steps of requesting a JWT token from a third party oauth provider. `src` should contain the full URL of the authorization URL for the 3rd party provider. Once the user has logged into the 3rd party provider the `onchange` event will be fired containing an event-like object whose data contains the JWT information. -The component need only be place into the DOM in order to begin the OAuth dance. - -### See +## See - [Component Source Code](./index.js) -- [Template Source Code](./index.hbs) --- diff --git a/ui/packages/consul-ui/app/components/jwt-source/index.js b/ui/packages/consul-ui/app/components/jwt-source/index.js index c4da73b58..664e5fcf7 100644 --- a/ui/packages/consul-ui/app/components/jwt-source/index.js +++ b/ui/packages/consul-ui/app/components/jwt-source/index.js @@ -1,31 +1,46 @@ -import Component from '@ember/component'; +import Component from '@glimmer/component'; import { inject as service } from '@ember/service'; + import { fromPromise } from 'consul-ui/utils/dom/event-source'; -export default Component.extend({ - repo: service('repository/oidc-provider'), - dom: service('dom'), - tagName: '', - onchange: function(e) {}, - onerror: function(e) {}, - init: function() { - this._super(...arguments); - this._listeners = this.dom.listeners(); - }, - willDestroyElement: function() { - this._super(...arguments); - this.repo.close(); - this._listeners.remove(); - }, - didInsertElement: function() { +// TODO: We could probably update this to be a template only component now +// rather than a JS only one. +export default class JWTSource extends Component { + @service('repository/oidc-provider') repo; + @service('dom') dom; + + constructor() { + super(...arguments); if (this.source) { this.source.close(); } + this._listeners = this.dom.listeners(); // TODO: Could this use once? Double check but I don't think it can - this.source = fromPromise(this.repo.findCodeByURL(this.src)); + this.source = fromPromise(this.repo.findCodeByURL(this.args.src)); this._listeners.add(this.source, { message: e => this.onchange(e), error: e => this.onerror(e), }); - }, -}); + } + + onchange(e) { + if(typeof this.args.onchange === 'function') { + this.args.onchange(...arguments); + } + } + + onerror(e) { + if(typeof this.args.onerror === 'function') { + this.args.onerror(...arguments); + } + } + + willDestroy() { + super.willDestroy(...arguments); + if (this.source) { + this.source.close(); + } + this.repo.close(); + this._listeners.remove(); + } +} diff --git a/ui/packages/consul-ui/app/components/token-source/README.mdx b/ui/packages/consul-ui/app/components/token-source/README.mdx index 03a2facd3..8a451f2c8 100644 --- a/ui/packages/consul-ui/app/components/token-source/README.mdx +++ b/ui/packages/consul-ui/app/components/token-source/README.mdx @@ -1,36 +1,89 @@ ---- -class: ember ---- -## TokenSource +# TokenSource -```hbs - `). It is also loosely based on a HTML `` +element (it has `type` and `value` arguments/attributes) + +When using the `oidc` type the component will go through the steps of +requesting the OIDC providers authorization URL from Consul, go through the +OIDC flow with the user and the 3rd party provider (via our `` +component), then lastly exchanging the resulting JWT with Consul for a normal +Consul token. + +When using the `secret` type, the component simply exchanges the users secret +for a normal Consul token. + +This component **does not store the resulting token**, it only emits it via +its `onchange` argument/event handler. Errors are emitted via the `onerror` +argument/event handler. + +```hbs preview-template +
+
Provide a widget to login with
+ + + +
+ +
+
When the button is clicked add TokenSource to the page
+ +{{#if (eq this.state 'authenticating')}} + +{{/if}} +
+
+
Show the results
+
+{{#if this.token}} +
AccessorID
+
{{this.token.AccessorID}}
+{{/if}} +{{#if this.error}} +
Error
+
{{this.error.detail}}
+{{/if}} +
+
``` -### Arguments +## Arguments | Argument | Type | Default | Description | | --- | --- | --- | --- | | `dc` | `String` | | The name of the current datacenter | | `nspace` | `String` | | The name of the current namespace | | `partition` | `String` | | The name of the current partition | +| `type` | `String` | | `secret` or `oidc`. `secret` is just traditional login, whereas `oidc` uses the users OIDC provider | +| `value` | `String` | | When `type` is `secret` this should be the users secret. When `type` is `oidc` this should be the name of the `AuthMethod` to use for authentication | | `onchange` | `Function` | | The action to fire when the data changes. Emits an Event-like object with a `data` property containing the jwt data, in this case the autorizationCode and the status | | `onerror` | `Function` | | The action to fire when an error occurs. Emits ErrorEvent object with an `error` property containing the Error. | -This component will go through the steps of requesting a JWT token from a third party oauth provider. `src` should contain the full URL of the authorization URL for the 3rd party provider. Once the user has logged into the 3rd party provider the `onchange` event will be fired containing an event-like object whose data contains the JWT information. -The component need only be place into the DOM in order to begin the OAuth dance. - -### See +## See +- [JwtSource Component](../jwt-source/README.mdx) +- [StateChart](./chart.xstate.js) - [Component Source Code](./index.js) - [Template Source Code](./index.hbs) diff --git a/ui/packages/consul-ui/app/components/token-source/index.hbs b/ui/packages/consul-ui/app/components/token-source/index.hbs index 30224b669..94186be1b 100644 --- a/ui/packages/consul-ui/app/components/token-source/index.hbs +++ b/ui/packages/consul-ui/app/components/token-source/index.hbs @@ -1,44 +1,60 @@ - - - {{!-- This `or` can be completely removed post 1.10 as 1.10 has optional params with default values --}} - {{#let (concat '/' (or partition '') '/' (or nspace '') '/' dc) as |path|}} + + + {{#let + (uri '/${partition}/{$nspace}/${dc}' + (hash + partition=@partition + nspace=@nspace + dc=@dc + ) + ) + as |path|}} {{/let}} diff --git a/ui/packages/consul-ui/app/components/token-source/index.js b/ui/packages/consul-ui/app/components/token-source/index.js index c0ad688c5..1eb317369 100644 --- a/ui/packages/consul-ui/app/components/token-source/index.js +++ b/ui/packages/consul-ui/app/components/token-source/index.js @@ -1,37 +1,47 @@ -import Component from '@ember/component'; +import Component from '@glimmer/component'; +import { action } from '@ember/object'; +import { tracked } from '@glimmer/tracking'; import chart from './chart.xstate'; -export default Component.extend({ - onchange: function() {}, - init: function() { - this._super(...arguments); + +export default class TokenSource extends Component { + @tracked provider; + @tracked jwt; + + constructor() { + super(...arguments); this.chart = chart; - }, - actions: { - isSecret: function() { - return this.type === 'secret'; - }, - change: function(e) { - e.data.toJSON = function() { - return { - AccessorID: this.AccessorID, - // TODO: In the past we've always ignored the SecretID returned - // from the server and used what the user typed in instead, now - // as we don't know the SecretID when we use SSO we use the SecretID - // in the response - SecretID: this.SecretID, - Namespace: this.Namespace, - Partition: this.Partition, - ...{ - AuthMethod: typeof this.AuthMethod !== 'undefined' ? this.AuthMethod : undefined, - // TODO: We should be able to only set namespaces if they are enabled - // but we might be testing for nspaces everywhere - // Namespace: typeof this.Namespace !== 'undefined' ? this.Namespace : undefined - }, - }; + } + + @action + isSecret() { + return this.args.type === 'secret'; + } + + @action + change(e) { + e.data.toJSON = function() { + return { + AccessorID: this.AccessorID, + // TODO: In the past we've always ignored the SecretID returned + // from the server and used what the user typed in instead, now + // as we don't know the SecretID when we use SSO we use the SecretID + // in the response + SecretID: this.SecretID, + Namespace: this.Namespace, + Partition: this.Partition, + ...{ + AuthMethod: typeof this.AuthMethod !== 'undefined' ? this.AuthMethod : undefined, + // TODO: We should be able to only set namespaces if they are enabled + // but we might be testing for nspaces everywhere + // Namespace: typeof this.Namespace !== 'undefined' ? this.Namespace : undefined + }, }; - // TODO: We should probably put the component into idle state - this.onchange(e); - }, - }, -}); + }; + // TODO: We should probably put the component into idle state + if(typeof this.args.onchange === 'function') { + this.args.onchange(e); + } + } + +}