Commit Graph

76 Commits

Author SHA1 Message Date
ac00386a29 web/admin: better footer links (#12004)
* web: Add InvalidationFlow to Radius Provider dialogues

## What

- Bugfix: adds the InvalidationFlow to the Radius Provider dialogues
  - Repairs: `{"invalidation_flow":["This field is required."]}` message, which was *not* propagated
    to the Notification.
- Nitpick: Pretties `?foo=${true}` expressions: `s/\?([^=]+)=\$\{true\}/\1/`

## Note

Yes, I know I'm going to have to do more magic when we harmonize the forms, and no, I didn't add the
Property Mappings to the wizard, and yes, I know I'm going to have pain with the *new* version of
the wizard. But this is a serious bug; you can't make Radius servers with *either* of the current
dialogues at the moment.

* First things first: save the blueprint that initializes the test runner.

* Committing to having the PKs be a string, and streamlining an event handler.  Type solidity needed for the footer control.

* web/admin/better-footer-links

# What

- A data control that takes two string fields and returns the JSON object for a FooterLink
- A data control that takes a control like the one above and assists the user in entering a
  collection of such objects.

# Why

We're trying to move away from CodeMirror for the simple things, like tables of what is essentially
data entry. Jens proposed this ArrayInput thing, and I've simplified it so you define what "a row"
is as a small, lightweight custom Component that returns and validates the datatype for that row,
and ArrayInput creates a table of rows, and that's that.

We're still working out the details, but the demo is to replace the "Name & URL" table in
AdminSettingsForm with this, since it was silly to ask the customer to hand-write JSON or YAML,
getting the keys right every time, for an `Array<Record<{ name: string, href: string }>>`. And some
client-side validation can't hurt.

Storybook included.  Tests to come.

* Not ready for prime time.

* One lint.  Other lints are still in progress.

* web: lots of 'as unknown as Foo'

I know this is considered bad practice, but we use Lit and Lit.spread
to send initialization arguments to functions that create DOM
objects, and Lit's prefix convention of '.' for object, '?' for
boolean, and '@' for event handler doesn't map at all to the Interface
declarations of Typescript.  So we have to cast these types when
sending them via functions to constructors.

* web/admin/better-footer-links

# What

- Remove the "JSON or YAML" language from the AdminSettings page for describing FooterLinks inputs.
- Add unit tests for ArrayInput and AdminSettingsFooterLinks.
- Provide a property for accessing a component's value

# Why

Providing a property by which the JSONified version of the value can be accessed enhances the
ability of tests to independently check that the value is in a state we desire, since properties can
easily be accessed across the wire protocol used by browser-based testing environments.

* Ensure the UI is built from _current_ before running tests.
2024-11-18 13:17:21 +01:00
a8b33b25e8 web/admin: improve error handling (#11212)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-09-05 01:31:06 +02:00
79c01ca473 web: update to ESLint 9 (#10812)
* web: update to ESLint 9

ESLint 9 has been out for awhile now, and all of the plug-ins that we use have caught up, so it is
time to bite the bullet and upgrade.  This commit:

- upgrades to ESLint 9, and upgrades all associated plugins
- Replaces the `.eslintrc` and `.eslintignore` files with the new, "flat" configuration file,
  "eslint.config.mjs".
- Places the previous "precommit" and "nightmare" rules in `./scripts/eslint.precommit.mjs` and
  `./scripts/eslint.nightmare.mjs`, respectively
- Replaces the scripted wrappers for eslint (`eslint`, `eslint-precommit`) with a single executable
  that takes the arguments `--precommit`, which applies a stricter set of rules, and `--nightmare`,
  which applies an even more terrifyingly strict set of rules.
- Provides the scripted wrapper `./scripts/eslint.mjs` so that eslint can be run from `bun`, if one
  so chooses.
- Fixes *all* of the lint `eslint.config.mjs` now finds, including removing all of the `eslint`
  styling rules and overrides because Eslint now proudly leaves that entirely up to Prettier.

To shut Dependabot up about ESLint.

* Added explanation for no-console removal.

* web: did not need the old and unmaintained nightmare mode; it can be configured directly.
2024-08-07 15:04:18 -07:00
d0a459076b web: enhance search select with portal, overflow, and keyboard controls (#9517)
* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: enhance search select

Patternfly doesn't even *have* a setting for "selected but not hovered," so I had to invent one. I
borrowed a trick from MUI and used the light blue "info" color, making it darker on
"selected+hovered."

This commit starts the revision process for search select. The goal is to have it broken down into
four major components: The inline-DOM component, which just shows the current value (or placeholder,
if none) and creates the portal for the floating component, then have a higher-level component for
the SearchSelect behavior, and a sidecar to manage the keyboard interactivity.

This is the portaled component: the actual list.

* web: enhance search select. Break menu and Input items into separate handlers.

* web: search select: added keyboard controller.

* web: search select - the isolation continues

This commit brings us to the position of having an independently rendered menu that listens for
click events on its contents, records the value internally *and* sends it upstream as an event.

This commit also includes a KeyboardController reactor that listens for keyboard events on a list of
objects, moving the focus up and down and sending a both a "selected" event when the user presses
Enter or Space, and a "close" event when the user presses Escape.

A lot of this is just infrastructure.  None of these *do* very much; they're just tools for making
SearchSelect better.

AkSearchSelectView is next: it's responsible for rendering the input and menu items, and then for
forwarding the `value` component up to whoever cares.

`ak-search-select` will ultimately be responsible for fetching the data and mapping the string
tokens from AkSearchSelectView back into the objects that Authentik cares about.

* web: search select - a functioning search select

So search select is now separated into the following components:

- SearchSelectView: Takes the renderables and the selected() Value and draws the Value in a
  box, then forwards the Options to a portaled drop-down component.
- SearchSelectMenuPosition: A web component that renders the Menu into the <BODY> element and
  positions it with respect to an anchor provided by SearchSelectView.
- SearchSelectMenu: Renders the Menu and listens for events indicating an Item has been selected.
  Sends events through a reference to the View.
- SearchKeyboardController: A specialized listener that keeps an independent list of indices and
  tabstops, and listens for keyboard events to move the index forward or backward, as well as for
  Event or Space for "select" and Escape for "close". Doesn't actually _do_ these things; they're
  just semantics implied by the event names, it just sends an event up to the host, which can do
  what it wants with them.

What's not done:

- SearchSelect: The interface with the API.  Maps to and from API values to renderable Options.

One thing of note: of the 35 uses of SearchSelect in our product, 28 of them have `renderElement`
annotations of a single field. Six of them use the same annotation (renderFlow), and only one (in
EventMatcherPolicyForm) is at all complex.  The 28 are:

- 7: group.name;
- 1: item.label;
- 5: item.name;
- 1: policy.name;
- 1: role.name;
- 1: source.name;
- 3: stage.name;
- 9: user.username;

I propose to modify `.renderElement` to take a string of `keyof T`, where T is the type passed to the
SearchSelect; it will simply look that up in the object passed in and use that as the Label.

`.renderDescription` is more or less similar, except it has _no_ special cases:

- 6: html`${flow.name}`;
- 1: html`${source.verboseName}`;
- 9: html`${user.name}`;
- 2: html`${flow.slug}`;

Given that, it makes sense to modify this as well to take a field key as a look up and render it,
making all that function calling somewhat moot.

Selected has a similar issue; passing it a value that is _not_ a function would be a signal to find
this specific element in the corresponding 'pk'.  Or we could pass a tuple of [keyof T] and value,
so we didn't have to hard-code 'pk' into the thing.

- 1             return item.pk === this.instance?.createUsersGroup;
- 1             return item.pk === this.instance?.filterGroup;
- 2             return item.pk === this.instance?.group;
- 1             return item.pk === this.instance?.parent;
- 1             return item.pk === this.instance?.searchGroup;
- 1             return item.pk === this.instance?.syncParentGroup;
- 1             return item.pk === this.instance?.policy;
- 1             return item.pk === this.instance?.source;
- 1             return item.pk === this.instance?.passwordStage;
- 1             return item.pk === this.instance?.stage;
- 1             return item.pk === this.instance?.user;
- 2             return item.pk === this.previewUser?.pk;
- 5             return item.pk === this.instance?.configureFlow;
- 1             return item.pk === this.instance?.mapping;
- 1             return item.pk === this.instance?.nameIdMapping;
- 1             return item.pk === this.instance?.user;
- 1             return item.pk === this.instance?.webhookMapping;
- 1             return item.component === this.instance?.action;
- 1             return item.path === this.instance?.path;
- 1             return item.name === this.instance?.model;
- 1             return item.name === this.instance?.app;
- 1             return user.pk.toString() === this.request?.toString();
- 2             return this.request?.user.toString() === user.pk.toString();

And of course, `.value` kinda sorta has the same thing going on:

- 6: flow?.pk;
- 3: group ? group.pk : undefined;
- 4: group?.pk;
- 1: item?.component;
- 2: item?.name;
- 1: item?.path;
- 4: item?.pk;
- 1: policy?.pk;
- 1: role?.pk;
- 1: source?.pk;
- 3: stage?.pk;
- 8: user?.pk;
- 1: user?.username;

All in all, the _protocol_ for SearchSelect could be streamlined. A _lot_. And still retain the
existing power.

* Old take; not keeping.

* Didn't need this either.

* web: search select - a functioning search select with API interface

So many edge cases!

Because the propagation here is sometimes KeyboardEvent -> MenuEvent -> SearchSelectEvent, I had to
rename some of the events to prevent them from creating infinite loops of event handling.  This
resulted in having to define separate events for Input, Close, and Select.

I struggled like heck to get the `<input>` object to show the value after updating. Ultimately, I
had to special case the `updated()` method to make sure it was showing the currently chosen display
value.  Looking through Stack Overflow, there's a lot of contention about the meaning of the `value`
field on HTMLInputElements.

The API layer distinguishes between a "search" event, which triggers the query to run, and the
"select" event, which triggers the component to pick an object as _the_ `.value`.

The API layer handles the conversion to GroupedItems and makes sure that the View receives either
FlatSelect or GroupedSelect options collections (see ./types, but in practice users should never
care too much about this.)

* web: completed the search select update

* web: search-select reveals a weakness in our plans

While testing SearchSelect, I realized that the protocol for our "custom input elements" was
neither specified nor documented.  I have attempted to fix that, and am finding edge cases
and buggy implementations that required addressing.

I've described the protocol by creating a class that implements it: AkControlElement.  It
extends the constructor to always provide the "this is an data-ak-control element," and
provides a `json()` method that throws an exception in the base class, so it must always
be overriden and never called as super().

I've also fixed ak-dual-select so it carries its name properly into the Forms parser.

* web: search select (and friends)

This commit finalizes the search select quest! Headline: Search Select is now keyboard-friendly
*and* CSS friendly; the styling needed for position is small enough to fit in a `styleMap`, and the
styling for the menu itself can be safely locked into a web component.

Primarily, I was forgetting to map the value to its displayValue whenever the value was changed from
an external source. It should have been an easy catch, but I missed it the first dozen times
through.

* Not using this yet.  ESLint-9 experiment that was loosely left here for some reason.

* Added lots of comments.

* Added new comments, fixed error message.

* Removing a console.log

* Fixed an incorrect comment.

* Added comments about workaround.

* web: focus fixes.

Fixes several issues with the drop-down, including primarily how "loss of focus"
does not result in the pop-up being banished. Also, the type definition for the
attribute `hidden` is inconsistent between Typescript, the attribute, and the
related property; I've chosen to route around that problem by using a custom
attribute and setting `hidden` in the template, where `lit-analyze` has a workable
definition and allows it to pass. Finally, on `open` the focus is passed to the
current value, if any.
2024-07-15 17:54:06 -07:00
ee58cf0c1c web: add HTMLTagNameElementMaps to everything to activate lit analyzer (#10217)
* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: add more linting

* A reliable test for the extra code needed in analyzer, passing shellcheck

* web: re-enable custom-element-manifest and enable component checking in Typescript

This commit includes a monkeypatch to allow custom-element-manifest (CEM) to work correctly again
despite our rich collection of mixins, reactive controllers, symbol-oriented event handlers, and the
like. With that monkeypatch in place, we can now create the CEM manifest file and then exploit it so
that IDEs and the Typescript compilation pass can tell when a component is being used incorrectly;
when the wrong types are being passed to it, or when a required attribute is not initialized.

* Added building the manifest to the build process, rather than storing it.  It is not appreciably slow.

* web: the most boring PR in the universe: Add HTMLTagNameElementMap to everyhing

This commit adds HTMLTagNameElementMap entries to every web component in the front end. Activating
and associating the HTMLTagNamElementMap with its class has enabled
[LitAnalyzer](https://github.com/runem/lit-analyzer/tree/master/packages/lit-analyzer) to reveal a
*lot* of basic problems within the UI, the most popular of which is "missing import." We usually get
away with it because the object being imported was already registered with the browser elsewhere,
but it still surprises me that we haven't gotten any complaints over things like:

```
./src/flow/stages/base.ts
Missing import for <ak-form-static>
96:  <ak-form-static
no-missing-import
```

Given how early and fundamental that seems to be in our code, I'd have expected to hear _something_
about it.

I have not enabled most of the possible checks because, well, there are just a ton of warnings when
I do.  I'd like to get in and fix those.

Aside from this, I have also _removed_ `customElement` declarations from anything declared as an
`abstract class`. It makes no sense to try and instantiate something that cannot, by definition, be
instantiated.  If the class is capable of running on its own, it's not abstract, it just needs to be
overridden in child classes.  Before removing the declaration I did check to make sure no other
piece of code was even *trying* to instantiate it, and so far I have detected no failures.  Those
elements were:

- elements/forms/Form.ts
- element-/wizard/WizardFormPage.ts

The one that blows my mind, though, is this:

```
src/elements/forms/ProxyForm.ts
6-@customElement("ak-proxy-form")
7:export abstract class ProxyForm extends Form<unknown> {
```

Which, despite being `abstract`, is somehow instantiable?

```
src/admin/outposts/ServiceConnectionListPage.ts:    <ak-proxy-form
src/admin/providers/ProviderListPage.ts:    <ak-proxy-form
src/admin/sources/SourceWizard.ts:    <ak-proxy-form
src/admin/sources/SourceListPage.ts:    <ak-proxy-form
src/admin/providers/ProviderWizard.ts:    <ak-proxy-form type=${type.component}></ak-proxy-form>
src/admin/stages/StageListPage.ts:    <ak-proxy-form
```

I've made a note to investigate.

I've started a new folder where all of my one-off tools for *how* a certain PR was run.  It has a
README describing what it's for, and the first tool, `add-htmlelementtagnamemaps-to-everything`, is
its first entry.  That tool is also documented internally.

``` Gilbert & Sullivan

I've got a little list,
I've got a little list,
Of all the code that would never be missed,
The duplicate code of cute-and-paste,
The weak abstractions that lead to waste,
The embedded templates-- you get the gist,
There ain't none of 'em that will ever be missed,
And that's why I've got them on my list!

```
2024-07-15 10:54:22 -07:00
ef2a40ed7d web: fix value handling inside controlled components (#9648)
* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: fix value handling inside controlled components

This is one of those stupid bugs that drive web developers crazy. The basics are straightforward:
when you cause a higher-level component to have a "big enough re-render," for some unknown
definition of "big enough," it will re-render the sub-components. In traditional web interaction,
those components should never be re-rendered while the user is interacting with the form, but in
frameworks where there's dynamic re-arrangement, part or all of the form could get re-rendered at
any mmoment. Since neither the form nor any of its intermediaries is tracking the values as they're
changed, it's up to the components themselves to keep the user's input-- and to be hardened against
property changes coming from the outside world.

So static memoization of the initial value passed in, and aggressively walling off the values the
customer generates from that field, are needed to protect the user's work from any framework's
dynamic DOM management. I remember struggling with this in React; I had hoped Lit was better, but in
this case, not better enough.

The protocol for "is it an ak-data-control" is "it has a `json()` method that returns the data ready
to be sent to the authentik server."  I missed that in one place, so that's on me.

* Eslint had opinions.

* Added comments to explain something.
2024-05-10 09:50:07 -07:00
2997382df2 core: fix logic for token expiration (#9426)
* core: fix logic for token expiration

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* bump default token expiration

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix frontend

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-04-25 15:42:58 +02:00
5805ac83f7 web: clean up and remove redundant alias '@goauthentik/app' (#8889)
* web: fix esbuild issue with style sheets

Getting ESBuild, Lit, and Storybook to all agree on how to read and parse stylesheets is a serious
pain. This fix better identifies the value types (instances) being passed from various sources in
the repo to the three *different* kinds of style processors we're using (the native one, the
polyfill one, and whatever the heck Storybook does internally).

Falling back to using older CSS instantiating techniques one era at a time seems to do the trick.
It's ugly, but in the face of the aggressive styling we use to avoid Flashes of Unstyled Content
(FLoUC), it's the logic with which we're left.

In standard mode, the following warning appears on the console when running a Flow:

```
Autofocus processing was blocked because a document already has a focused element.
```

In compatibility mode, the following **error** appears on the console when running a Flow:

```
crawler-inject.js:1106 Uncaught TypeError: Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'.
    at initDomMutationObservers (crawler-inject.js:1106:18)
    at crawler-inject.js:1114:24
    at Array.forEach (<anonymous>)
    at initDomMutationObservers (crawler-inject.js:1114:10)
    at crawler-inject.js:1549:1
initDomMutationObservers @ crawler-inject.js:1106
(anonymous) @ crawler-inject.js:1114
initDomMutationObservers @ crawler-inject.js:1114
(anonymous) @ crawler-inject.js:1549
```

Despite this error, nothing seems to be broken and flows work as anticipated.

* web: clean up and remove redundant alias '@goauthentik/app'

The path alias `@goauthentik/app` has been a thorn in our side for a long time, as it conflicts with
or is redundant with all the *other* aliases in `tsconfig.json`, such as `@goauthentik/elements` and
`@goauthentik/locales`.

This commit *replaces* `@goauthentik/app` with `@goauthentik/authentik` for a single use case: the
locale codes file in the project root.  That also helps reserve the subproject name `authentik` in
case we ever do go the monorepo root.

Other than that, all the rest have been removed with the following mechanical refactor:

```
perl -pi.bak -e 's{\@goauthentik/app/}{\@goauthentik/}' $(rg -l '@goauthentik/app/' ./src/)
```

* web: separate the sizing enum from a specific component implementation (#8890)

The PFSizes enum is used by more than just the Spinner, but has been left inside the Spinner for all
this time, making refactoring the Spinner for Patternfly 5 a little harder (okay, an annoying amount
harder) than it should be.

This commit moves this UI-specific, widely-use enum into its own folder in `common`, and refactors
everything else to use it.  As is often the case, the refactor is mechanical:

```
perl -pi.bak -e 's{import \{ PFSize \} from "\@goauthentik/elements/Spinner";}{import \{ PFSize \}
from "\@goauthentik/common/enums.js";}' \\
    $(rg -l 'import.*PFSize')
```

**Note:** This commit is dependent upon the ["clean up and remove redundant alias `@goauthentik/app`" PR](https://github.com/goauthentik/authentik/pull/8889)
2024-03-14 10:10:42 -07:00
5f1ba45966 web: provide dual-list multiselect with pagination (#8004)
* web: revise css-import-maps to need only a single entry, rather than dual-entry

Given that the difference Vite/Storybook cares about is whether or not there's a
sigil at the end of the CSS string, it seemed silly to require devs to enter
both the raw and sigiled string; just do an in-line text-and-replace.

* web: provide a "select / select all" tool for the dual list multiselect

**This commit**

Provides one of several of the sub-controls needed to make the multi-list multi-select thing work.
This is the simplest control, and I decided to go with it first because it's all presentation; all
it does is show the buttons and send events from those buttons.

A Storybook component is provided to show how well it works.

* web: provide a "select / select all" tool for the dual list multiselect

**This commit**

This commit provides the following new features for dual list multiselect:

- The "available" pane, which has all of the entries that are available to be selected.  Items that
  are already selected will remain, but they're marked with a checkmark and can neither be selected
  or moved.
- The "selected" pane, which has *all* of the entries that have been selected.
- The Pagination control, which in this case only sends an event upstream.

**Plan**:

The plan is to have a master control that marries the available-pane, selected-pane,
select-controls, and pagination-controls into a single component that receives the list of
"currently visible" available entries and keeps the list of "currently selected" entries, as well as
a pass-through for the pagination value that allows it to hide the pagination control if there is
only one page.

A master component *above that* will provide the list of currently visible entries and, at need,
read the value of the master control object for the "selected" list. That component will mostly be
data-only; it's render will probably just be `<slot></slot>`; its duty will be only to map entries
to string keys Lit can use, and to provide the lists we want to provide and the pagination ranges we
want to show.

Some judicious use of grid will allow me size the controls properly with/without the pagination
control.

Status and Title are going to be in the master control.

A <slot> will be provided for Search, but I have no plans to integrate that into this control as of
yet.

There is already a planned fallback control; the multi-select experience on mobile is actually
excellent, and we should exploit that appropriately.

* web: provide a "select / select all" tool for the dual list multiselect

**This commit**

1. Re-arrange the contents of the folder so that the sub-components are in their own folder. This
   reduces the clutter and makes it easier to understand where to look for certain things.
2. Re-arranges the contents of the folder so that all the Storybook stories are in their own folder.
   Again, this reduces the clutter; it also helps the compiler understand what not to compile.
3. Strips down the "Available items pane" to a minimal amount of interactivity and annotates the
   passed-in properties as `readonly`, since the purpose of this component is to display those. The
   only internal state kept is the list of items marked-to-move.
4. Does the same thing with the "Selected items pane".
5. Added comments to help guide future maintainers.
6. Restructured the CSS, taking a _lot_ of it into our own hands. Patternfly continues to act as if
   all components are fully available all the time, and that's simply not true in a shadowDOM
   environment. By separating out the global CSS Custom Properties from the grid and style
   definitions of `pf-c-dual-list-selector`, I was able to construct a more simple and
   straightforward grid (with nested grids for the columns inside).
7. Added "Delete ALL Selected" to the controls
8. Added "double-click" as a "move this one NOW" feature.

* web: provide a "select / select all" tool for the dual list multiselect

**This commit**

- Fixes the bug whereby pagination would leave the 'some moves available' state visible by clearing
  the 'to-move' state when the list of options changes.
- Fixes the bug whereby a change of 'options' in available would also cause an update to
  `selectedKeys`, causing the entire selected field to clear. Fixed by making `selectedKeys` a
  static object updated only when `selected` is generated rather than generating it anew with each
  re-rerender. (Hey, kids, can you say "functional programming and immutability" five time fast? I
  knew you could!)
- Fixes the bug whereby the change of outpost type would not cause an update of the `options`
  collection.
- Fixes the bug whereby the CSS was not creating enough whitespace separation between the whole
  component and its siblings. Host components are coded `span:static` unless otherwise styled to be
  `block`; we want `block` most of the time.
- Fixes the bug whereby the list of existing objects wasn't being passed to the handler correctly.
- Updates the Form Handler to recognize this new input object.
- Fixes the bug whereby changing outpost type doesn't handle the list of selected applications well.
- Fixes the bug whereby the identity of the outpost type's associated `fetch()` function loses
  identity -- necessary to maintain the selected outpost type switch.
- Fixes the CSS bug whereby horizontal scrolling would not enable correctly when the application's
  name overflows the listbox.
- Completes this assignment.  :-)

* web: last-minute pre-commit cleanup.

* running localize extract

* web: codeql found an issue with one of my tests.

* web: multi-select

Modified the display so that if it's a template we display it
correctly opposite the text, and provide classes that can be used
in the display to differentiate between the main label and the
descriptive label.

Added a sort key, so the select can sort the right-hand pane correctly.

Fixed the `this.selected` setters to use Arrays instead of maps.
Theoretically, this is terribly inefficient, as it makes it
theoretically O(n^2) rather than O(1), but in practice even if both
lists were 10,000 elements long a modern desktop could perform the
entire scan in 150ms or so.

* fix lint error

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update strings slightly

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* start on dark theme support

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* web: Add searchbar and enable it for "selected"

"Available" requires a round-trip to the provider level, so that's next.

* web: provide a search for the dual list multiselect

**This commit**

- Includes a new widget that represents the basic, Patternfly-designed search bar.  It just emits
  events of search request updates.
- Changes the definition of a data provider to take an optional search string.
- Changes the handler in the *independent* layer so that it catches search requests and those
  requests work on the "selected" collection.
- Changes the handler of the `authentik` interface layer so that it catches search requests and
  those requests are sent to the data provider.
- Provides a debounce function for the `authentik` interface layer to not hammer the Django instance
  too much.
- Updates the data providers in the example for `OutpostForm` to handle search requests.
- Provides a property in the `authentik` interface layer so that the debounce can be tuned.

* web: always trim the search string passed.

* web: code quality pass, extra comments, pre-commit check.

* Serious (and bizarre) merge bug.  I guess it doesn't like XML that much.

* Attempting to reason with whatever eslint GitHub is using.

* Prettier has opinions.

* Enable better dark mode.

There were two issues: the dark mode didn't reach into the "search"
bar, and there were several hover states that weren't handled well.

This commit handles both.  The color scheme mirrors the one we
currently use, but it's a bit backwards from Patternfly 5.  Dunno
how we're gonna reconcile all that.

* Prettier fixes and locale extraction

* web: update pagination type to use generic, provided type

* web: fixed a few comment typos

* Discordant version numbers for @go-authentik/api were causing build failures.

* What is up with CI/CD?

* web: missed a lint issue that prevented the build from running successfully

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-01-25 10:08:00 -08:00
afdc7d241f web/admin: revise wizard form handling (#7331)
* web: break circular dependency between AKElement & Interface.

This commit changes the way the root node of the web application shell is
discovered by child components, such that the base class shared by both
no longer results in a circular dependency between the two models.

I've run this in isolation and have seen no failures of discovery; the identity
token exists as soon as the Interface is constructed and is found by every item
on the page.

* web: fix broken typescript references

This built... and then it didn't?  Anyway, the current fix is to
provide type information the AkInterface for the data that consumers
require.

* web: extract the form processing from the form submission process

Our forms have a lot of customized value handling, and the function `serializeForm` takes
our input structures and creates a JSON object ready for submission across the wire for
the various models provided by the API.

That function was embedded in the `ak-form` object, but it has no actual dependencies on
the state of that object; aside from identifying the input elements, which is done at the
very start of processing, this large block of code stands alone.  Separating out the
"processing the form" from "identifying the form" allows us to customize our form handling
and preserve form information on the client for transactional purposes such as our wizard.

w

* web: multi-select, but there's a styling issue.

* web: provide a closed control for multi-select

This commit creates a new control, using the ak-form-element-horizontal as a *CLOSED*
object, for our multi-select.  This control right now is limited to what we expect to
be using in the wizard, but that doesn't mean it can't be smarter in the future.

* web: hung up by a silly spelling error

* web: update the form-handling method

With the `serializeForm` method extracted, it's much easier to examine and parse
every *form* with every keystroke, preserving them against the changes that
happen as the customer navigates the Wizard.  With that in place, it became
straightforward to retrofit the "handle changes to the application, to the provider, and to the providerType"
into the three pages of the wizard, and to provide *all* of the form elements in a base class
such that no specialized handling needs to happen to any of the child pages.

Fixed an ugly typo in the oauth2 provider, as well.

* web: wizard should work with multi-select and should reflect default values

(Note: This commit is predicated on both the "Extract serializeForm function from Form.ts" and
"Provide a controlled multi-select input control" PRs.)

The initial attempt at the wizard was woefully naive in its implementation, missing some critical
details along the way.  This revision starts off with one stronger assumption: trust that Jens knows
what he's doing, and knew what he was building when he wrote the initial `Form` handler.

The problem with the `Form` handler, and the reason I avoided it, was simply that it does too many
things, especially in its ModelForm variant: it receives a model from the back-end, renders a
(hand-written) form for that model, allows the user to interact with that model, and facilitates
saving it to the back-end again, complete with on-page notifications of success or failure.

The Wizard could not use all of that. It needs to gather the information for *two* models (an
Application and a Provider, plus the ProviderType) and has a new and specialized end-point for a
transaction that allows the committing or roll back of both models to happen simultaneously,
predicated on success or failure respectively.

With "Extract `serializeForm` completed, it was possible to repurpose the forms that already
existed, stripping them down to just their input components, and eventing the entire thing in a
single event loop of "events flow up, data flows down." In this case, the *entire form* is
serialized on a per-event basis and pushed up the to the orchestration layer, which saves them off.
Writing a parent `BasePanel` class that has accessors for `formValues` and `valid` means that the
state of every page is accessible with a simple query. This simplified the `BaseProviderPanel` class
to just specialize the `dispatchUpdate` method to send the wizard update with the new provider
information filled out.

Because the *form* is being treated as the source of truth about the state of a `Partial<Application>`
or `Partial<*Provider>` object, the defaults are now being captured as expected.

Likewise, this simplified the `providerCache` layer which preserves customer input in the event that
the customer starts filling out the wrong provider to a simple conditional clause in the
orchestrator. The Wizard has much fewer smarts because it doesn't (and probably never did) need
them.

Along with the above changes, the following has also been done:

For SAML and SCIM, the providerMappings now works.  They weren't being managed as `state` objects,
so they weren't receiving updates when the update event retrieved the information from the back-end.
In order to make clear what's happening, I have extracted the loops from the original definition and
built them as named objects: `propertyMappings`, `pmUserValues`, `pmGroupValues` and so on, which I
then pass into the new multi-select component.

I fixed a really embarrassing typo in Oauth2's "advanced settings" block.

I have extracted the CoreGroup search-select into a custom component.

I deleted the `merge` function.  That was a faulty experiment with non-deterministic outcomes, and I
was never happy with it.  I'm glad its gone.

I've added a title header to each of the providers, so the user can be sure that they're looking
at the right provider type when they start filling out the form.

I've created a new token, `data-ak-control`, with which we can mark all objects that we can treat as
Authentik value-producing components, the form value of which is available through a `json()`
method.  I've added this bit of intelligence to the `serializeForm` function, short-circuiting the
complex processing and putting the "this is the shape of the value we expect from this input" *onto
the input itself*.  Which is where it belongs.

* web: add error handling to wizard.

* web: improve error handling in light components

Rather than reproduce the error handling across all of the LightComponents,
I've made a parent class that takes the common fields to distribute between
the ak-form-element-horizontal and the input object itself.  This made it
much easier to properly display errors in freeform input fields in the
wizard, as well as working with the routine error handling in Form.ts

* Added the radio control to the list of LightComponents.

* Fix bug where event was recorded twice.

* Fixed merge bug (?) that somehow deleted the Authorization Select block in OAuth2.

* web: prettier had opinions

* web: added error handling and display

* web: bump @lit-labs/context from 0.4.1 to 0.5.1 in /web

Bumps [@lit-labs/context](https://github.com/lit/lit/tree/HEAD/packages/labs/context) from 0.4.1 to 0.5.1.
- [Release notes](https://github.com/lit/lit/releases)
- [Changelog](https://github.com/lit/lit/blob/main/packages/labs/context/CHANGELOG.md)
- [Commits](https://github.com/lit/lit/commits/@lit-labs/context@0.5.1/packages/labs/context)

---
updated-dependencies:
- dependency-name: "@lit-labs/context"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* web: updated wizard to run with latest package.json configuration

Apparently, there were stale dependencies in package-lock.json that were conflicting
with the requests in our package.json.  By running `npm update`, I was able to resolve
the conflict.

I have also removed the default names from the context names collection; they weren't doing
any good, and they permit frictionless renaming of dependencies, which is never a good
idea.

* web: schlepping on the errors messages

During testing, I realized I was unhappy with the error messages. They're not very helpful.
By adding links to navigate back to the place where the error occurred, and providing better
context for what the error could have been, I hope to help the use correct their errors.

* make package the same as main

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-12-06 13:28:19 +02:00
a52e4a3262 web: extract form processing (#7298)
* web: break circular dependency between AKElement & Interface.

This commit changes the way the root node of the web application shell is
discovered by child components, such that the base class shared by both
no longer results in a circular dependency between the two models.

I've run this in isolation and have seen no failures of discovery; the identity
token exists as soon as the Interface is constructed and is found by every item
on the page.

* web: fix broken typescript references

This built... and then it didn't?  Anyway, the current fix is to
provide type information the AkInterface for the data that consumers
require.

* web: extract the form processing from the form submission process

Our forms have a lot of customized value handling, and the function `serializeForm` takes
our input structures and creates a JSON object ready for submission across the wire for
the various models provided by the API.

That function was embedded in the `ak-form` object, but it has no actual dependencies on
the state of that object; aside from identifying the input elements, which is done at the
very start of processing, this large block of code stands alone.  Separating out the
"processing the form" from "identifying the form" allows us to customize our form handling
and preserve form information on the client for transactional purposes such as our wizard.

w
2023-10-25 14:39:50 -07:00
b503379319 web: fix form default submit handler (#7122)
* web/elements: rename renderInlineForm to renderForm set submit handler to empty function

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix all kinds of forms not using the form inheritance correctly

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-10-12 15:12:46 +02:00
3b171a02b7 web: laying the groundwork for future expansion (#7045)
* web: laying the groundwork for future expansion

This commit is a hodge-podge of updates and changes to the web.  Functional changes:

- Makefile: Fixed a bug in the `help` section that prevented the WIDTH from being accurately
  calculated if `help` was included rather than in-lined.

- ESLint: Modified the "unused vars" rule so that variables starting with an underline are not
  considered by the rule.  This allows for elided variables in event handlers.  It's not a perfect
  solution-- a better one would be to use Typescript's function-specialization typing, but there are
  too many places where we elide or ignore some variables in a function's usage that switching over
  to specialization would be a huge lift.

- locale: It turns out, lit-locale does its own context management.  We don't need to have a context
  at all in this space, and that's one less listener we need to attach t othe DOM.

- ModalButton: A small thing, but using `nothing` instead of "html``" allows lit better control over
  rendering and reduces the number of actual renders of the page.

- FormGroup: Provided a means to modify the aria-label, rather than stick with the just the word
  "Details."  Specializing this field will both help users of screen readers in the future, and will
  allow test suites to find specific form groups now.

- RadioButton: provide a more consistent interface to the RadioButton.  First, we dispatch the
  events to the outside world, and we set the value locally so that the current `Form.ts` continues
  to behave as expected.  We also prevent the "button lost value" event from propagating; this
  presents a unified select-like interface to users of the RadioButtonGroup.  The current value
  semantics are preserved; other clients of the RadioButton do not see a change in behavior.

- EventEmitter: If the custom event detail is *not* an object, do not use the object-like semantics
  for forwarding it; just send it as-is.

- Comments: In the course of laying the groundwork for the application wizard, I throw a LOT of
  comments into the code, describing APIs, interfaces, class and function signatures, to better
  document the behavior inside and as signposts for future work.

* web: permit arrays to be sent in custom events without interpolation.

* actually use assignValue or rather serializeFieldRecursive

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-10-02 13:33:27 -07:00
d35c7df789 web: detangle element to admin references (#6864)
* Web: Detangling some circular dependencies in Admin and User

Admin, User, and Flow should not dependend upon each other, at least
not in a circular way.  If Admin and User depend on Flow, that's
fine, but Flow should not correspondingly depend upon elements of
either; if they have something in common, let's put them in
`@goauthentik/common` or find some other smart place to store them.

This commit refactors the intentToLabel and actionToLabel functions
into `@goauthentik/common/labels` and converts them to static tables
for maintenance purposes.

* web: "Consistency is the hobgoblin of small minds" - Ralph Waldo Emerson

* web: I found these confusing to look at, so I added comments.

* web: remove admin-to-user component reference(s)

There was only one: AppIcon.  This has been moved to `components`.

Touching the LibraryApplications page triggered a cyclomatic
complexity check.  Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.

* web: remove admin from elements

This commit removes the two references from `elements` to `admin`: the list of UserEvents and a
reference to the FlowSearch type, used by the Forms manager to decide how to extract a value.
For FlowSearch, a different convention for detecting the type was implemented (instances of the
object have a unique fieldname for the value holder).  UserEvents and ObjectChangelog have been
moved to `components` as they're clearly dependent upon the API.

This defers work on removing Admin from Components, as that is (again) references going the
wrong way, but that can happen later.

* web: remove admin-to-user component reference(s) (#6856)

There was only one: AppIcon.  This has been moved to `components`.

Touching the LibraryApplications page triggered a cyclomatic
complexity check.  Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.

* This was supposed to be merged.
2023-09-13 12:28:42 -07:00
2b9dc4ccd8 web/admin: fix circular dependency (#6740)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-09-03 03:12:20 +02:00
3f02534eb1 web: weightloss program, part 1: FlowSearch (#6332)
* web: weightloss program, part 1: FlowSearch

This commit extracts the multiple uses of SearchSelect for Flow lookups in the `providers`
collection and replaces them with a slightly more legible format, from:

```HTML
<ak-search-select
    .fetchObjects=${async (query?: string): Promise<Flow[]> => {
        const args: FlowsInstancesListRequest = {
            ordering: "slug",
            designation: FlowsInstancesListDesignationEnum.Authentication,
        };
        if (query !== undefined) {
            args.search = query;
        }
        const flows = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(args);
        return flows.results;
    }}
    .renderElement=${(flow: Flow): string => {
        return RenderFlowOption(flow);
    }}
    .renderDescription=${(flow: Flow): TemplateResult => {
        return html`${flow.name}`;
    }}
    .value=${(flow: Flow | undefined): string | undefined => {
        return flow?.pk;
    }}
    .selected=${(flow: Flow): boolean => {
        return flow.pk === this.instance?.authenticationFlow;
    }}
>
</ak-search-select>
```

... to:

```HTML
<ak-flow-search
    flowType=${FlowsInstancesListDesignationEnum.Authentication}
    .currentFlow=${this.instance?.authenticationFlow}
    required
></ak-flow-search>
```

All of those middle methods, like `renderElement`, `renderDescription`, etc, are *completely the
same* for *all* of the searches, and there are something like 25 of them; this commit only covers
the 8 in `providers`, but the next commit should carry all of them.

The topmost example has been extracted into its own Web Component, `ak-flow-search`, that takes only
two arguments: the type of `FlowInstanceListDesignation` and the current instance of the flow.

The static methods for `renderElement`, `renderDescription` and `value` (which are all the same in
all 25 instances of `FlowInstancesListRequest`) have been made into standalone functions.
`fetchObjects` has been made into a method that takes the parameter from the `designation` property,
and `selected` has been turned into a method that takes the comparator instance from the
`currentFlow` property.  That's it.  That's the whole of it.

`SearchSelect` now emits an event whenever the user changes the field, and `ak-flow-search`
intercepts that event to mirror the value locally.

`Form` has been adapted to recognize the `ak-flow-search` element and extract the current value.

There are a number of legibility issues remaining, even with this fix.  The Authentik Form manager
is dependent upon a component named `ak-form-element-horizontal`, which is a container for a single
displayed element in a form:

```HTML
<ak-form-element-horizontal
    label=${msg("Authorization flow")}
    ?required=${true}
    name="authorizationFlow"
>
    <ak-flow-search
        flowType=${FlowsInstancesListDesignationEnum.Authorization}
        .currentFlow=${this.instance?.authorizationFlow}
        required
    ></ak-flow-search>
    <p class="pf-c-form__helper-text">
        ${msg("Flow used when authorizing this provider.")}
    </p>
</ak-form-element-horizontal>
```

Imagine, instead, if we could write:

```HTML
<ak-form-element-flow-search
    flowType=${FlowsInstancesListDesignationEnum.Authorization}
    .currentFlow=${this.instance?.authorizationFlow}
    required
    name="authorizationFlow">
<label slot="label">${msg("Authorization flow")}</label>
<span slot="help">${msg("Flow used when authorizing this provider.")}</span>
<ak-form-element-flow-search>
```

Starting with a superclass that understands the need for `label` and `help` slots, it would
automatically configure the input object that would be used.  We've already specified multiple
identical copies of this thing in multiple different places; centralizing their definition and then
re-using them would be classic code re-use.

Even better, since the Authorization flow is used 10 times in the whole of our code base, and the
Authentication flow 8 times, and they are *all identical*, it would be fitting if we just created
wrappers:

```HTML
<ak-form-element-flow-search
    flowType=${FlowsInstancesListDesignationEnum.Authorization}>
<ak-form-element-flow-search>
```

That's really all that's needed. There are *hundreds* (about 470 total) cases where nine or more
lines of repetitious HTML could be replaced with a one-liner like the above.

A "narrow waist" design is one that allows for a system to communicate between two different
components through a small but consistent collection of calls. The Form manager needs to be narrowed
hard. The `ak-form-element-horizontal` is a wrapper around an input object, and it has this at its
core for extracting that information. This forwards the name component to the containing input
object so that when the input object generates an event, we can identify the field it's associated
with.

```Javascript
this.querySelectorAll("*").forEach((input) => {
    switch (input.tagName.toLowerCase()) {
        case "input":
        case "textarea":
        case "select":
        case "ak-codemirror":
        case "ak-chip-group":
        case "ak-search-select":
        case "ak-radio":
            input.setAttribute("name", this.name);
            break;
        default:
            return;
    }
```

A *temporary* variant of this is in the `ak-flow-search` component, to support this API without
having to modify `ak-form-element-horizontal`.

And then `ak-form` itself has this:

```Javascript
if (
    inputElement.tagName.toLowerCase() === "select" &&
    "multiple" in inputElement.attributes
) {
    const selectElement = inputElement as unknown as HTMLSelectElement;
    json[element.name] = Array.from(selectElement.selectedOptions).map((v) => v.value);
} else if (
    inputElement.tagName.toLowerCase() === "input" &&
    inputElement.type === "date"
) {
    json[element.name] = inputElement.valueAsDate;
} else if (
    inputElement.tagName.toLowerCase() === "input" &&
    inputElement.type === "datetime-local"
) {
    json[element.name] = new Date(inputElement.valueAsNumber);
}
// ... another 20 lines removed
```

This ought to read:

```Javascript
const json = elements.filter((element => element instanceof AkFormComponent)
    .reduce((acc, element) => ({ ...acc, [element.name]: element.value] });
```

Where, instead of hand-writing all the different input objects for date and datetime and checkbox
into our forms, and then having to craft custom value extractors for each and every one of them,
just write *one* version of each with all the wrappers and bells and whistles already attached, and
have each one of them have a `value` getter descriptor that returns the value expected by our form
handler.

A back-of-the-envelope estimation is that there's about four *thousand* lines that could disappear
if we did this right.

More importantly, it would be possible to create new `AkFormComponent`s without having to register
them or define them for `ak-form`; as long as they conformed to the AkFormComponent's expectations
for "what is a source of values for a Form", `ak-form` would understand how to handle it.

Ultimately, what I want is to be able to do this:

``` HTML
<ak-input-form
   itemtype="ak-search"
   itemid="ak-authentication"
   itemprop=${this.instance}></ak-inputform>
```

And it will (1) go out and find the right kind of search to put there, (2) conduct the right kind of
fetch to fill that search, (3) pre-configure it with the user's current choice in that locale.

I don't think this is possible-- for one thing, it would be very expensive in terms of development,
and it may break the "narrow waist" ideal by require that the `ak-input-form` object know all the
different kinds of searches that are available.  The old Midgardian dream was that the object would
have *just* the identity triple (A table, a row of that table, a field of that row), and the
Javascript would go out and, using the identity, *find* the right object for CRUD (Creating,
Retrieving, Updating, and Deleting) it.

But that inspiration, as unreachable as it is, is where I'm headed.  Where our objects are both
*smart* and *standalone*.  Where they're polite citizens in an ordered universe, capable of
independence sufficient to be tested and validated and trusted, but working in concert to achieve
our aims.

* web: unravel the search-select for flows completely.

This commit removes *all* instances of the search-select
for flows, classifying them into four different categories:

- a search with no default
- a search with a default
- a search with a default and a fallback to a static default if non specified
- a search with a default and a fallback to the tenant's preferred default if this is a new instance
  and no flow specified.

It's not humanly possible to test all the instances where this has been committed, but the linters
are very happy with the results, and I'm going to eyeball every one of them in the github
presentation before I move this out of draft.

* web: several were declared 'required' that were not.

* web: I can't believe this was rejected because of a misspelling in a code comment. Well done\!

* web: another codespell fix for a comment.

* web: adding 'codespell' to the pre-commit command. Fixed spelling error in eventEmitter.
2023-07-28 22:57:14 +02:00
03f3ad89df web: bump prettier from 2.8.8 to 3.0.0 in /web (#6329)
* web: bump prettier from 2.8.8 to 3.0.0 in /web

Bumps [prettier](https://github.com/prettier/prettier) from 2.8.8 to 3.0.0.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/2.8.8...3.0.0)

---
updated-dependencies:
- dependency-name: prettier
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* update formatting and config

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-07-21 18:19:19 +02:00
b0fbd576fc security: cure53 fix (#6039)
* ATH-01-001: resolve path and check start before loading blueprints

This is even less of an issue since 411ef239f6, since with that commit we only allow files that the listing returns

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-010: fix missing user filter for webauthn device

This prevents an attack that is only possible when an attacker can intercept HTTP traffic and in the case of HTTPS decrypt it.

* ATH-01-008: fix web forms not submitting correctly when pressing enter

When submitting some forms with the Enter key instead of clicking "Confirm"/etc, the form would not get submitted correctly

This would in the worst case is when setting a user's password, where the new password can end up in the URL, but the password was not actually saved to the user.

* ATH-01-004: remove env from admin system endpoint

this endpoint already required admin access, but for debugging the env variables are used very little

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-003 / ATH-01-012: disable htmlLabels in mermaid

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-005: use hmac.compare_digest for secret_key authentication

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-009: migrate impersonation to use API

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-010: rework

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-014: save authenticator validation state in flow context

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

bugfixes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ATH-01-012: escape quotation marks

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add website

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update release ntoes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* update with all notes

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix format

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-06-22 22:25:04 +02:00
52132112f6 web/elements: don't send value from writeOnly field that hasn't been modified (#5614)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-05-15 19:45:36 +02:00
6437fbc814 web/admin: prompt preview (#5078)
* add initial prompt preview

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* improve error handling

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't flood api with requests when fields are changeed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add tests

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-03-25 22:31:48 +01:00
14f0034a0a web/elements: only render form once instance is loaded (#5049)
* web/elements: only render form once instance is loaded

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* use radio for transport

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* only wait for instance to be loaded if set

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* add hook to load additional data in form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make send an abstract function instead of attribute

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* ensure form is updated after data is loaded

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove until for select and multi-selects in forms

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* don't use until for file uploads

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove last until from form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove deprecated import

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* prevent form double load, add error handling for PreventFormSubmit

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* fix double creation of inner element in proxy form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* make PreventFormSubmit work correctly

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-03-23 14:05:14 +01:00
32840d3909 web/admin: fix error adding users to groups (#5016)
* web/admin: fix error adding users to groups

and vice versa

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove seed

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-03-20 18:15:36 +01:00
9dbd54690c web/elements: fix search select inconsistency (#4989)
* web/elements: fix search-select inconsistency

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* web/common: fix config having to be json converted everywhere

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* web/elements: refactor form without iron-form

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* web/admin: fix misc

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-03-17 13:48:34 +01:00
b6b820f6f1 web: toggle dark/light theme manually (#4876) 2023-03-09 23:17:53 +01:00
ddbd8153e2 web: migrate checkbox to switch (#4409)
* start migrating to switch

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* general cleanup

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* remove broken Create provider

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate all

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

* migrate table selectors, fix dark theme

Signed-off-by: Jens Langhammer <jens@goauthentik.io>

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-01-11 13:37:49 +01:00
ba5cd6e719 web/admin: add Radio control, search-select fixes (#4333)
* move search select to forms folder

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add radio, migrate smaller lists

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* move dropdown when scrolling, hide when container out of frame

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2023-01-02 14:51:44 +01:00
9564894eda web/elements: trigger search select data update on connected callback
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2023-01-02 10:26:52 +01:00
b9bb27008e web/elements: fix selection of blank elements in search-select, fix issue when re-opening dropdown
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2023-01-01 20:10:16 +01:00
4a91a7d2e2 web: re-organise frontend and cleanup common code (#3572)
* fix repo in api client

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: re-organise files to match their interface

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* core: include version in script tags

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* cleanup maybe broken

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* revert rename

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: get rid of Client.ts

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* move more to common

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* more moving

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* format

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* unfuck files that vscode fucked, thanks

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* move more

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* finish moving (maybe)

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* ok more moving

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix more stuff that vs code destroyed

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* get rid "web" prefix for virtual package

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix locales

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* use custom base element

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix css file

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* don't run autoDetectLanguage when importing locale

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix circular dependencies

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: fix build

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-15 00:05:21 +02:00
11334cf638 web: re-cleanup imports not being absolute
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-09-06 19:02:40 +02:00
417156d659 web/elements: fix automatic slug not working on newly opened forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-08-18 19:36:56 +02:00
504338ea66 web/admin: application wizard (part 1) (#2745)
* initial

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* remove log

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* start oauth

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* use form for all type wizard pages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* more oauth

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* basic wizard actions

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* make resets work

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add hint in provider wizard

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* render correct icon in empty state in table page

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* improve empty state

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* more

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add more pages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* fix

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add group PK to service account creation response

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* use wizard-level isValid prop

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* re-add old buttons

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-06-26 00:46:40 +02:00
a8c04f96d2 web: use absolute imports with path rewrite instead of relative imports (#3149) 2022-06-25 17:44:17 +02:00
1c62a3db6e core: user paths (#3085)
* init

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add user_path_template

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add to sources and flow

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add outposts & api

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* dark theme for treeview

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add search

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add docs and tests for validation

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add to user write stage

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add web ui

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: improve error handling

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-06-15 12:12:26 +02:00
abc73deda0 web/elements: fix error with blank SearchSelect elements in forms
closes #2469

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-11 20:36:54 +01:00
4f4f954693 core: customisable user settings (#2397)
* tenants: add user_settings flow, add basic flow and basic new executor

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: use flow PromptStage instead of custom stage

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/flows: add tenant to StageHost interface

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: fix form missing component

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: re-add success message

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: improve support for multiple error messages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/prompt: allow expressions in prompt placeholders

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/prompt: add tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* flows: always set pending user

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* flows: never cache stage configuration flow plans

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/user_write: fix error when pending user is anonymous user

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add checkbox for prompt placeholder expression

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* website/docs: add prompt expression docs

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/prompt: add ak-locale field type

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* tenants: fix default policy

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: add function to do global refresh

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/flows: fix rendering of ak-locale

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* tenants: fix default policy, add error handling to placeholder, fix locale attribute

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* add tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-03-03 00:13:06 +01:00
61f7db314a web/admin: use searchable select field for users and groups in policy binding form
closes #2285

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2022-02-26 17:49:04 +01:00
c77f804b77 web/user: fix user details not rendering when loading to a different user settings tab and then switching
closes #1664

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-12-22 20:13:52 +01:00
e587c53e18 web: remove deprecated rollup-plugin-node-resolve
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-11-04 22:34:48 +01:00
73733b20b6 build(deps): bump @trivago/prettier-plugin-sort-imports from 2.0.4 to 3.0.0 in /web (#1684)
* build(deps): bump @trivago/prettier-plugin-sort-imports in /web

Bumps [@trivago/prettier-plugin-sort-imports](https://github.com/trivago/prettier-plugin-sort-imports) from 2.0.4 to 3.0.0.
- [Release notes](https://github.com/trivago/prettier-plugin-sort-imports/releases)
- [Changelog](https://github.com/trivago/prettier-plugin-sort-imports/blob/master/CHANGELOG.md)
- [Commits](https://github.com/trivago/prettier-plugin-sort-imports/commits)

---
updated-dependencies:
- dependency-name: "@trivago/prettier-plugin-sort-imports"
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

* web: update prettier config

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-10-28 09:48:51 +02:00
aef9d27706 stages/authenticator_sms: Add SMS Authenticator Stage (#1577)
* stages/authenticator_sms: initial implementation

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add initial stage UI

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/elements: clear invalid state when old input was invalid but new input is correct

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/authenticator_sms: add more logic

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/user: add basic SMS settings

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/authenticator_sms: initial working version

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/authenticator_sms: add tests

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/flows: optimise totp password manager entry on authenticator_validation stage

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/elements: add grouping support for table

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: allow sms class in authenticator stage

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web/admin: add grouping to more pages

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* stages/authenticator_validate: add SMS support

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* api: add throttling for flow executor based on session key and pending user

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* web: fix style issues

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>

* ci: add workflow to compile backend translations

Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-10-11 17:51:49 +02:00
d0bfb99859 web/elements: improve error handling on forms
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-24 12:19:56 +02:00
da7635ae5c web: sort imports
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-21 11:33:51 +02:00
a92a0fb60a web: migrate to lit 2
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-21 11:19:26 +02:00
663dffd8be web/admin: fix error in firefox when creating token
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-19 00:28:40 +02:00
92ce5f0931 web: improve error display when only {'detail'} is returned
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-09-02 19:55:37 +02:00
0ccec96490 core: make user optional in token creation
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-25 21:21:51 +02:00
8022d0801d web/elements: add support for datetime-local
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-25 21:14:32 +02:00
1d4b941a3b web: migrate to @goauthentik/api
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-15 21:32:28 +02:00
cdf57d7eea web/elements: add support for dot-notation in input names
Signed-off-by: Jens Langhammer <jens.langhammer@beryju.org>
2021-08-11 19:48:19 +02:00