Commit Graph

60 Commits

Author SHA1 Message Date
3fa6ce2e34 enterprise/web/admin: OSM for events (#9287)
* 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.

* initial OSM for events

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

* remove card title

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

* split with volume

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

* add pin

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

* basic map selection

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

* update pin

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

* rewrite map points to be more imperative

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

* zoom to fit

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
2025-06-18 13:36:40 +02:00
f025d0d1d5 enterprise/search: ability to use more precise search queries (#7698)
* api: use DjangoQL for searches

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

* expand search input and use textarea for multiline

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

* start implementing autocomplete

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

* only use ql for events

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

* make QL search opt in

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

* fix

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

* make pretend json relation work

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

* fix schema

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

* test

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

* format

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

* make autocomplete l1 work

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

* use forked js lib with types, separate QL

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

* first attempt at making it fit our UI

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

* make dark theme somewhat work, fix search

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

* make more parts work

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

* make auto complete box be under cursor

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: ripplefcl <github@ripple.contact>

* remove django autocomplete for now

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

* re-add event filtering

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

* fix search when no ql is enabled

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

* make meta+enter submit, fix colour

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

* make dark theme

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

* formatting

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

* enterprise

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

* fix tests

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

* add tests

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

* Update authentik/enterprise/search/apps.py

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>

* add json element autocomplete

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: ripplefcl <github@ripple.contact>

* format

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

* fix

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

* fix query

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

* fix search reset

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

* fix dark theme

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: ripplefcl <github@ripple.contact>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2025-06-18 12:23:00 +02:00
bc4b07d57b web/admin: remove all special cases of slug handling, replace with a "smart slug" component (#14983)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/components: Remove all special cases of slug handling, replace with a "smart slug" component

This commit removes all special handling for the `slug` attribute in our text. A variant of the text
input control that can handle formatting-as-slugs has replaced all the slugificiation code; simply
drop it onto a page and tell it the (must be unique) selector from which to get the data to be
slugified. It only looks up one tier of the DOM so be careful that both the text input and its slug
accessory occupy the same DOM context.

## Details

### The Component

Now that we know a (lot) more about Lit, this component has been slightly updated to meet our
current standards.

- web/src/components/ak-slug-input.ts

Changes made:

- The "listen for the source object" has been moved to the `firstUpdated`, so that it no longer has
  to wait for the end of a render.
 - The `dirtyFlag` handler now uses the `@input` syntax.
- Updated the slug formatter to permit trailing dashes.
- Uses the `@bound` decorator, eliminating the need to do binding in the constructor (and so
  eliminating the constructor completely).

### Component uses:

The following components were revised to use `ak-slug-input` instead of a plain text input with the
slug-handling added by our forms manager.

- applications/ApplicationForm.ts
- flows/FlowForm.ts
- sources/kerberos/KerberosSourceForm.ts
- sources/ldap/LDAPSourceForm.ts
- sources/oauth/OAuthSourceForm.ts
- sources/plex/PlexSourceForm.ts
- sources/saml/SAMLSourceForm.ts
- sources/scim/SCIMSourceForm.ts

### Remove the redundant special slug handling code

- web/src/elements/forms/Form.ts
- web/src/elements/forms/HorizontalFormElement.ts

### A special case among special cases

- web/src/admin/stages/invitation/InvitationForm.ts

This form is our one case where we have a slug input field with no corresponding text source. Adding
a simple event handler to validate the value whenever it changed and write back a "clean" slug was
the most straightforward solution. I added a help line; it seemed "surprising" to ask someone for a
name and not follow the same rules as "names" everywhere else in our UI without explanation.

* After writing the commit message, I realized some of the comments I made MUST be added to the component.

* The `source` attribute needed its own comment to indicate that a `query()` compatible selector is expected.

* Added public/private/protected/# indicators to all fields.  Trying to balance between getting it 'right' and leaving an opening for harmonizing style-sharing and state-sharing between (text / textarea), slug, password and (visible / hidden / secret).

* Removed the ids as requested; the default "look for this" matches the original behavior without requiring it be hard-coded and unchangable.
2025-06-16 09:04:00 -07:00
e85d2d0096 Web/cleanup/empty state better slot handling (#14289)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web: remove Lit syntax from always true attributes

## What

Replaces instances of `?loading=${true}` and `?loading="${true}"` with `loading`

## Why

The Lit syntax is completely unnecessary when the attribute's state is constant, and it's a few
(just a few) extra CPU cycles for Lit to process that.

More to the point, it annoys me.

## How

```
$ perl -pi.bak -e 's/\?loading=\$\{true\}/loading/' $(rg -l '\?loading=\$\{true\}')
$ find . -name '*.bak' -exec rm {} \;
$ perl -pi.bak -e 's/\?loading="\$\{true\}"/loading/' $(rg -l '\?loading="\$\{true\}"')
$ find . -name '*.bak' -exec rm {} \;
```

* Prettier had opinions

* web: move optional textual information out of attributes and into slots

## What

Replaces instances of:

```
<ak-empty-state header=${msg(...)}></ak-empty-state>
```

with

```
<ak-empty-state><span slot="header">${msg(...)}</span></ak-empty-state>
```

## Why

1. It's correct.
2. It lets us elide the decorations for any slots we aren't using.
3. It's preparation for moving to Patternfly 5
4. It annoyed me.

## How

Since we already have Patternfly Elements installed, we have access to the PFE-Core, which has the
unbelievable useful `SlotsController`.  Using it, I created a conditional render template that will
only put in the header, body, and primary slots if there is something in the lightDOM requesting
those slots.  The conditional template will still put the spinner in if the header is not provided
but the loading state is true.

I then had to edit all the places where this is used. For about 30 of them, this script sufficed:

```
perl -pi.bak -e 's/header="?(\$\{msg\([^)]+\)\})"?>/><span slot="header">\1<\/span>/' \
     $(rg -l `<ak-empty-state[^>]header')

```

The other six had to be done by hand.  I have tested a handful of the automatic ones, and all of the
ones that were edited manually.  I'm pleasantly surprised that the textual rules [are inherited by
the slots as expected](https://htmlwithsuperpowers.netlify.app/styling/inheritable.htm).
2025-06-16 08:17:11 -07:00
3c2ce40afd web/admin: Text and Textarea Fields that "hide" their contents until prompted (#15024)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/admin: Provide `hidden` text and textarea components

## Details

This commit provides two new elements (technically, since they're API-unaware), one for `<input
type="text">`, and one for `<textarea>`, that provide for the ability to create fields that are (or
can be) hidden. A new boolean attribute, `revealed`, shows the state of the component (the content
is therefore *not* revealed by default).

It also includes a third new element, `ak-visibility-toggle`, that creates a hide/show toggle with
all the right icons, styling, and eventing.  It's straightforward, and isolating it improved the
DX of everything that uses that feature by quite a bit.

Storybook stories (with autodoc documentation) have been provided for `ak-hidden-text-input`,
`ak-hidden-textarea-input`, and `ak-visibility-toggle`.

## Maintenance Notice

As a maintenance detail, the field `ak-private-text` has been renamed `ak-secret-text` to reflect
its usage, and the places where it was used have all been changed to reflect that update.

* web/component: embed styling (for now) to handle the lightDom/shadowDom/slot conflicts in HorizontalLightComponent and HorizontalFormElement

* Comments and Types. I really shouldn't have to catch this stuff with my eyeballs.

* fix typo

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-14 01:48:34 +02:00
652a32d968 stages/email: Only attach logo to email if used (#14835)
* Only attach logo to email if used

* Fix MIME logo attachment to adhere to standard

* format, fix web

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

* not tuple

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2025-06-09 03:49:00 +02:00
a01bb551d0 web/standards: fix boolean attribute abuse (#14662)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/maintenance: correct the usage of boolean (false) attributes

## What

Just cleaning up a bad habit; we have a lot of `?attribute=${true|false}` (or, more alarmingly,
`.attribute=${true|false}`.  These should just be `attribute` or be missing; anything else is
unnecessary.

Where the attribute is `true` by default, no changes have been made; there are only a few of them,
and they require re-working of the logic to assist with the changes. Booleans should never be `true` by
default, and if you absolutely cannot find an alternative phrasing that makes having them be `false`
by default a valid choice, they should be `reflect: true` to make their presence visible to screen
readers and debuggers.

## Why

Removing non-standard HTML uses from web components matches our programming standards and is an
important step toward the Authentik Elements NPM package, as well as the Schema-Driven Forms update.

## Boring excessive detail.

Because there are literally hundreds of changes, I've documented the boring ones here.

Changes that do *not* meet the basic criteria of "made the component comply with the standards" are
commented in the PR.

Here are all the Boolean property declarations in the system, delta the ones that declare `= true`;
those are documented at the bottom of this commit, and are not addressed in this PR.  This
information is included to guide your decision making.  The second block, below, documents the
actual changes made to component declarations throughout our code.  The third block, at the bottom,
documents changes not made due to logic and effort constraints.

```
components/ak-switch-input.ts:
    @property({ type: Boolean })
    checked: boolean = false;
--
components/ak-switch-input.ts:
    @property({ type: Boolean })
    required = false;
--
components/ak-file-input.ts:
    @property({ type: Boolean })
    required = false;
--
components/HorizontalLightComponent.ts:
    @property({ type: Boolean })
    required = false;
--
components/ak-multi-select.ts:
    @property({ type: Boolean })
    required = false;
--
elements/TreeView.ts:
    @property({ type: Boolean })
    open = false;
--
components/ak-status-label.ts:
    @property({ type: Boolean })
    good = false;
--
components/ak-status-label.ts:
    @property({ type: Boolean })
    compact = false;
--
elements/CodeMirror.ts:
    @property({ type: Boolean })
    readOnly = false;
--
elements/buttons/ModalButton.ts:
    @property({ type: Boolean })
    open = false;
--
elements/EmptyState.ts:
    @property({ type: Boolean })
    fullHeight = false;
--
elements/Tabs.ts:
    @property({ type: Boolean })
    vertical = false;
--
elements/ak-checkbox-group/ak-checkbox-group.ts:
    @property({ type: Boolean })
    required = false;
--
elements/Label.ts:
    @property({ type: Boolean })
    compact = false;
--
elements/forms/FormGroup.ts:
    @property({ type: Boolean, reflect: true })
    expanded = false;
--
elements/Expand.ts:
    @property({ type: Boolean })
    expanded = false;
--
elements/forms/HorizontalFormElement.ts:
    @property({ type: Boolean })
    required = false;
--
elements/forms/HorizontalFormElement.ts:
    @property({ type: Boolean })
    slugMode = false;
--
elements/forms/SearchSelect/ak-portal.ts:
    @property({ type: Boolean, reflect: true })
    open = false;
--
elements/Alert.ts:
    @property({ type: Boolean })
    inline = false;
--
elements/forms/SearchSelect/ak-search-select-view.ts:
    @property({ type: Boolean, reflect: true })
    open = false;
--
elements/forms/SearchSelect/ak-search-select-view.ts:
    @property({ type: Boolean })
    blankable = false;
--
elements/sidebar/SidebarItem.ts:
    @property({ type: Boolean })
    expanded = false;
--
admin/stages/StageWizard.ts:
    @property({ type: Boolean })
    showBindingPage = false;
--
elements/forms/FormElement.ts:
    @property({ type: Boolean })
    required = false;
--
admin/common/ak-flow-search/FlowSearch.ts:
    @property({ type: Boolean })
    required?: boolean = false;
--
admin/applications/ProviderSelectModal.ts:
    @property({ type: Boolean })
    backchannel = false;
--
elements/forms/SearchSelect/SearchSelect.ts:
    @property({ type: Boolean })
    blankable = false;
--
admin/applications/components/ak-provider-search-input.ts:
    @property({ type: Boolean })
    required = false;
--
admin/applications/components/ak-provider-search-input.ts:
    @property({ type: Boolean })
    blankable = false;
--
admin/applications/components/ak-backchannel-input.ts:
    @property({ type: Boolean })
    required = false;
--
admin/policies/PolicyWizard.ts:
    @property({ type: Boolean })
    showBindingPage = false;
```

The attribute 'required' is an HTML native, and is false by default.

Here are all the change pairs around HTML attrbutes:

```
$ git diff | rg -A 1 '\?(backchannel|blankable|checked|compact|expanded|fullHeight|good|inline|open|readOnly|required|showBindingPage|slugMode|vertical)\b'

-                ?required=${true}
+                required

-                    <ak-provider-select-table ?backchannel=${true} .confirm=${this.confirm}>
+                    <ak-provider-select-table backchannel .confirm=${this.confirm}>

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${false}

-                    ?required=${true}
+                    required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                                  ?blankable=${true}
+                                  blankable

-                        ?required=${true}
+                        required

-                ?blankable=${true}
+                blankable

-                ?blankable=${true}
+                blankable

-            ?required=${true}
+            required

-            ?required=${true}
+            required

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-        return html` <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
+        return html` <ak-form-element-horizontal label=${msg("Name")} name="name" required>

-                ?required=${true}
+                required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                    ?blankable=${true}
+                    blankable

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("Severity")} ?required=${true} name="severity">
+            <ak-form-element-horizontal label=${msg("Severity")} required name="severity">

-                        ?showBindingPage=${true}
+                        showBindingPage

-                ?showBindingPage=${true}
+                showBindingPage

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Title")} ?required=${true} name="title">
+            <ak-form-element-horizontal label=${msg("Title")} required name="title">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            ?required=${true}
+            required

-            <ak-form-element-horizontal label=${msg("Stage")} ?required=${true} name="stage">
+            <ak-form-element-horizontal label=${msg("Stage")} required name="stage">

-            <ak-form-element-horizontal label=${msg("Order")} ?required=${true} name="order">
+            <ak-form-element-horizontal label=${msg("Order")} required name="order">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                    ?blankable=${true}
+                    blankable

-                ?required=${true}
+                required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Type")} ?required=${true} name="type">
+            <ak-form-element-horizontal label=${msg("Type")} required name="type">

-                    ?blankable=${true}
+                    blankable

-                        <ak-label color=${PFColor.Green} ?compact=${true}>
+                        <ak-label color=${PFColor.Green} compact>

-                            ? html`<ak-label color=${PFColor.Red} ?compact=${true}
+                            ? html`<ak-label color=${PFColor.Red} compact

-                            : html`<ak-label color=${PFColor.Green} ?compact=${true}
+                            : html`<ak-label color=${PFColor.Green} compact

-                    ? html`<ak-label color=${PFColor.Orange} ?compact=${true}>
+                    ? html`<ak-label color=${PFColor.Orange} compact>

-                    : html`<ak-label color=${PFColor.Green} ?compact=${true}>
+                    : html`<ak-label color=${PFColor.Green} compact>

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Docker URL")} ?required=${true} name="url">
+            <ak-form-element-horizontal label=${msg("Docker URL")} required name="url">

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?showBindingPage=${true}
+                        showBindingPage

-                      ?showBindingPage=${true}
+                      showBindingPage

-                            ?blankable=${true}
+                            blankable

-                            ?blankable=${true}
+                            blankable

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Order")} ?required=${true} name="order">
+            <ak-form-element-horizontal label=${msg("Order")} required name="order">

-            <ak-form-element-horizontal label=${msg("Timeout")} ?required=${true} name="timeout">
+            <ak-form-element-horizontal label=${msg("Timeout")} required name="timeout">

-                    ? html`<ak-label color=${PFColor.Green} ?compact=${true}>
+                    ? html`<ak-label color=${PFColor.Green} compact>

-                    : html`<ak-label color=${PFColor.Orange} ?compact=${true}>
+                    : html`<ak-label color=${PFColor.Orange} compact>

-        return html`<ak-form-element-horizontal label=${msg("User")} ?required=${true} name="user">
+        return html`<ak-form-element-horizontal label=${msg("User")} required name="user">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                            ?blankable=${true}
+                            blankable

-                            ?blankable=${true}
+                            blankable

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                      ?readOnly=${true}
+                      readOnly

-            return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
+            return html`<ak-empty-state loading fullHeight></ak-empty-state>`;

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                            ?blankable=${true}
+                            blankable

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                    ?required=${true}
+                    required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                            ?blankable=${true}
+                            blankable

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-            ?required=${true}
+            required

-                    ?required=${true}
+                    required

-                                        ?blankable=${true}
+                                        blankable

-            <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
+            <ak-form-element-horizontal label=${msg("Name")} name="name" required>

-            <ak-form-element-horizontal label=${msg("Protocol")} ?required=${true} name="protocol">
+            <ak-form-element-horizontal label=${msg("Protocol")} required name="protocol">

-            <ak-form-element-horizontal label=${msg("Host")} name="host" ?required=${true}>
+            <ak-form-element-horizontal label=${msg("Host")} name="host" required>

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                    ?required=${false}

-                        ?blankable=${true}
+                        blankable

-                        ?blankable=${true}
+                        blankable

-        return html`<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html`<ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                                      ?readOnly=${true}
+                                      readOnly

-                                    ?blankable=${true}
+                                    blankable

-        return html`<ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html`<ak-form-element-horizontal label=${msg("Name")} required name="name">

-                    : html`<ak-label color=${PFColor.Orange} ?compact=${true}>
+                    : html`<ak-label color=${PFColor.Orange} compact>

-                <ak-label color=${PFColor.Grey} ?compact=${true}> ${msg("Built-in")}</ak-label>
+                <ak-label color=${PFColor.Grey} compact> ${msg("Built-in")}</ak-label>

-            return html`<ak-empty-state loading ?fullHeight=${true}></ak-empty-state>`;
+            return html`<ak-empty-state loading fullHeight></ak-empty-state>`;

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${false}>
+            <ak-form-group>

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                            ?blankable=${true}
+                            blankable

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                ?required=${true}
+                required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-                        ?required=${true}
+                        required

-                                ?readOnly=${true}
+                                readOnly

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Slug")} ?required=${true} name="slug">
+            <ak-form-element-horizontal label=${msg("Slug")} required name="slug">

-            <ak-form-group ?expanded=${true}>
+            <ak-form-group expanded>

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                <ak-form-element-horizontal label=${msg("SMTP Host")} ?required=${true} name="host">
+                <ak-form-element-horizontal label=${msg("SMTP Host")} required name="host">

-                <ak-form-element-horizontal label=${msg("SMTP Port")} ?required=${true} name="port">
+                <ak-form-element-horizontal label=${msg("SMTP Port")} required name="port">

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                ?required=${true}
+                required

-                    ?blankable=${true}
+                    blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        <ak-alert ?inline=${true}>
+                        <ak-alert inline>

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                    <ak-form-element-horizontal label=${msg("Mode")} ?required=${true} name="mode">
+                    <ak-form-element-horizontal label=${msg("Mode")} required name="mode">

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                <ak-form-element-horizontal label=${msg("SMTP Host")} ?required=${true} name="host">
+                <ak-form-element-horizontal label=${msg("SMTP Host")} required name="host">

-                <ak-form-element-horizontal label=${msg("SMTP Port")} ?required=${true} name="port">
+                <ak-form-element-horizontal label=${msg("SMTP Port")} required name="port">

-                    ?required=${true}
+                    required

-                    ?required=${true}
+                    required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                ?slugMode=${true}
+                slugMode

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("Expires")} ?required=${true} name="expires">
+            <ak-form-element-horizontal label=${msg("Expires")} required name="expires">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                            ?blankable=${true}
+                            blankable

-                        ?required=${true}
+                        required

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Field Key")} ?required=${true} name="fieldKey">
+            <ak-form-element-horizontal label=${msg("Field Key")} required name="fieldKey">

-            <ak-form-element-horizontal label=${msg("Label")} ?required=${true} name="label">
+            <ak-form-element-horizontal label=${msg("Label")} required name="label">

-            <ak-form-element-horizontal label=${msg("Type")} ?required=${true} name="type">
+            <ak-form-element-horizontal label=${msg("Type")} required name="type">

-            <ak-form-element-horizontal label=${msg("Order")} ?required=${true} name="order">
+            <ak-form-element-horizontal label=${msg("Order")} required name="order">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Source")} ?required=${true} name="source">
+            <ak-form-element-horizontal label=${msg("Source")} required name="source">

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                        ?required=${true}
+                        required

-                        <ak-alert ?inline=${true}>
+                        <ak-alert inline>

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-                        ?required=${true}
+                        required

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-            <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+            <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                            ?blankable=${true}
+                            blankable

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("User")} ?required=${true} name="user">
+            <ak-form-element-horizontal label=${msg("User")} required name="user">

-            <ak-form-element-horizontal label=${msg("Intent")} ?required=${true} name="intent">
+            <ak-form-element-horizontal label=${msg("Intent")} required name="intent">

-                ?required=${true}
+                required

-                    <input class="pf-c-switch__input" type="checkbox" ?checked=${true} />
+                    <input class="pf-c-switch__input" type="checkbox" checked />

-                    <input class="pf-c-switch__input" type="checkbox" ?checked=${true} />
+                    <input class="pf-c-switch__input" type="checkbox" checked />

-                ?required=${true}
+                required

-            <ak-form-element-horizontal label=${msg("User type")} ?required=${true} name="type">
+            <ak-form-element-horizontal label=${msg("User type")} required name="type">

-            <ak-form-element-horizontal label=${msg("Path")} ?required=${true} name="path">
+            <ak-form-element-horizontal label=${msg("Path")} required name="path">

-            ?required=${true}
+            required

-            ?required=${true}
+            required

-            <ak-tabs pageIdentifier="userCredentialsTokens" ?vertical=${true}>
+            <ak-tabs pageIdentifier="userCredentialsTokens" vertical>

-                <ak-status-label ?good=${true}></ak-status-label>
+                <ak-status-label good></ak-status-label>

-                    ?open=${true}
+                    open

-                ?blankable=${true}
+                blankable

-                <ak-tabs ?vertical="${true}">
+                <ak-tabs vertical>

-        return html` <ak-form-element-horizontal label=${msg("Name")} ?required=${true} name="name">
+        return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">

-                ?required=${true}
+                required

```

The following issues are the `true` by default Booleans.  As mentioned, these are **not addressed** by this PR.

```
elements/table/Table.ts::
    @property({ type: Boolean })
    paginated = true;

elements/forms/ModalForm.ts::
    @property({ type: Boolean })
    closeAfterSuccessfulSubmit = true;

elements/forms/ModalForm.ts::
    @property({ type: Boolean })
    showSubmitButton = true;

elements/CodeMirror.ts::
    @property({ type: Boolean })
    parseValue = true;

elements/LoadingOverlay.ts::
    @property({ type: Boolean })
    loading = true;

admin/stages/authenticator_validate/AuthenticatorValidateStageForm.ts::
    @property({ type: Boolean })
    showConfigurationStages = true;

elements/user/sources/SourceSettings.ts::
    @property({ type: Boolean })
    canConnect = true;

admin/outposts/OutpostHealthSimple.ts::
    @property({ attribute: false })
    showVersion = true;

elements/wizard/Wizard.ts::
    @property({ type: Boolean })
    canCancel = true;

elements/wizard/Wizard.ts::
    @property({ type: Boolean })
    canBack = true;
```

* Prettier had opinions.

* Caught during code review.

* Merged incorrectly; not sure what went wrong, but this re-applies the removal of the  syntax from the current LDAPSourceForm.ts from  to this branch.
2025-06-06 23:06:25 +00:00
9a03bdeaf1 web/maintenance: remove writeOnly hacks from Form and HorizontalFormElement (#14649)
* 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.

* This (temporary) change is needed to prevent the unit tests from failing.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes

* Revert "This (temporary) change is needed to prevent the unit tests from failing."

This reverts commit dddde09be5.

* web/standards: use attribute naming scheme for attributes

## What

Renamed the 'inputHint' attribute to 'input-hint', because it is an attribute, not a property.
Properties are camelCased, but attributes are kebab-cased.

Updated all instances where this appears with the usual magic:

```
$ perl -pi.bak -e 's/inputHint="code"/input-hint="code"/' $(rg -l 'inputHint="code"')
```

This fix is in preparation for both the Patternfly 5 project and the Schema-Driven Forms project.

* web/maintenance: remove `writeOnly` hacks from Form and HorizontalFormElement

## What

The `writeOnly` hack substituted an obscuring, read-only field for secret keys and passwords that an
admin should never be able to see/read, only *write*, but allowed the user to click on and replace
the key or password. The hack performed this substitution within `HorizontalFormElement` and
dispersed a flag throughout the code to enforce it. Another hack within `Form` directed the API to
not update / write changes to that field if the field had never been activated.

This commit replaces the `writeOnly` hack with a pair of purpose-built components,
`ak-private-text-input` and `ak-private-textarea-input`, that perform the exact same functionality
but without having to involve the HorizontalFormElement, which really should just be layout and
generic functionality.  It also replaces all the `writeOnly` hackery in Form with a simple
`doNotProcess` flag, which extends and genericizes this capability to any and all input fields.

The only major protocol change is that `?writeOnly` was a *positive* flag; you controlled it by
saying `this.instance !== undefined`; `?revealed` is a *positive* flog; you reveal the working input
field when `this.instance === undefined`.

It is not necessary to specify the monospace, autocomplete, and spell-check features; those are
enabled or disabled automatically when the `input-hint="code"` flag is passed.

## Why

Removing special cases from processing code is an important step toward the Authentik Elements NPM
package, as well as the Schema-Driven Forms update.

## Note

This is actually a very significant change; this is important functionality that I have hand-tested
quite a bit, but could wish for automated testing that also checks the database back-end to ensure
the fixes made write the keys and passwords as required. Checking the back-end directly is important
since these fields are never re-sent to the front-end after being saved!

Things like `placeholder`, `required`, and getting the `name`, `label` or `help` are all issues very
subject to Last-Line Effect, so give this the hairiest eyeball you've got, please.

* Found a few small things, like a missing import that might have broken something.

* web/admin: Update `private-text` field to pass new linting requirement.

\# What

\# Why

\# How

\# Designs

\# Test Steps

\# Other Notes
2025-06-06 15:11:49 -07:00
6b530ff764 web/admin: use attribute naming scheme for attributes (#14644)
web/standards: use attribute naming scheme for attributes

## What

Renamed the 'inputHint' attribute to 'input-hint', because it is an attribute, not a property.
Properties are camelCased, but attributes are kebab-cased.

Updated all instances where this appears with the usual magic:

```
$ perl -pi.bak -e 's/inputHint="code"/input-hint="code"/' $(rg -l 'inputHint="code"')
```

This fix is in preparation for both the Patternfly 5 project and the Schema-Driven Forms project.

* Revision: You almost certainly do NOT want every field to be reflected.  Booleans are okay, especially for fields that may be changed internally but need to be read by screen-readers, sunch as 'hidden' or 'invalid'.  But if a field is a string or a number and has a default value, you do not want it to be reflected.
2025-06-06 14:49:47 -07:00
fb3ec1f38b web: minor design tweaks (#14803)
* fix spacing between header and page desc

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

* fix icon alignment

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

* fallback text when we dont have a user yet

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-06-01 21:01:43 +02:00
c28b65a3f2 Web: Controllers cleanup (#14616)
* web: Fix issues surrounding availability of controllers during init.

web: Fix edgecase where flow does not have brand.

* web: Fix import path.

* web: Clean up mixin/controller paths.

* web: Prepare for consistent import styling.

- Prep for Storybook fixes.

* web: Update MDX types.

* web: Fix issues surrounding async imports, MDX typing, relative paths.

* web: Format. Clarify.

* web: Group module types.
2025-05-26 07:06:14 -04:00
1c5e906a3e web/NPM Workspaces: ESbuild version cleanup (#14541)
* web: Check JS files. Add types.

* web: Fix issues surrounding Vite/ESBuild types.

* web: Clean up version constants. Tidy types

* web: Clean up docs, types.

* web: Clean up package paths.

* web: (ESLint) no-lonely-if

* web: Render slot before navbar.

* web: Fix line-height alignment.

* web: Truncate long headers.

* web: Clean up page header declarations. Add story. Update paths.

* web: Ignore out directory.

* web: Lint Lit.

* web: Use private alias.

* web: Fix implicit CJS mode.

* web: Update deps.

* web: await all imports.
2025-05-20 02:11:18 +02:00
d97297e0ce web: (ESLint) Consistent use of triple-equals. (#14554)
web: Consistent use of triple-equals.
2025-05-19 13:25:11 -04:00
0cf6bff93c tests/e2e: add test for authentication flow in compatibility mode (#14392)
* tests/e2e: add test for authentication flow in compatibility mode

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

* web: Add prefix class to CSS for easier debugging of constructed stylesheets.

- Use CSS variables for highlighter.

* web: Fix issue where MDX components apply styles out of order.

* web: Fix hover color.

* web: Fix CSS module types. Clean up globals.

* web: Fix issues surrounding availability of shadow root in compatibility mode.

* web: Fix typo.

* web: Partial fixes for storybook dark theme.

* web: Fix overflow.

* web: Fix issues surrounding competing interfaces attempting to apply styles.

* fix padding in ak-alert in. markdown

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

* web: Minimize use of sub-module exports.

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Teffen Ellis <teffen@sister.software>
2025-05-15 16:51:11 +02:00
f70635c295 web: Clean up browser-only module imports that crash WebDriverIO. (#14330)
* web: Clean up browser-only module imports that crash WebDriverIO.

* web: Clarify slug format output.
2025-05-02 20:04:05 -04:00
61a26c02b7 Revert-revert: Safari fixes (#14331)
* Reapply "web: Safari fixes merge branch (#14181)"

This reverts commit a41d45834c.

* web: Fix brand preference order. Adjust header height.
2025-05-02 21:26:40 +02:00
337956672f Revert "web: Safari fixes merge branch (#14181)" (#14211) 2025-04-24 14:00:29 -04:00
cf160f800d web: Safari fixes merge branch (#14181)
* web/admin: Fix layout centering. Adjust theming.

* web: Fix issue where references to Lit SSR break page styles.

* web: Fix issues surrounding color scheme/theme mixup in UI.
2025-04-24 10:16:04 -04:00
0e83de2697 web: Tidy temporal utilities. (#13755) 2025-04-07 18:37:03 +00:00
363d655378 web: Normalize client-side error handling (#13595)
web: Clean up error handling. Prep for permission checks.

- Add clearer reporting for API and network errors.
- Tidy error checking.
- Partial type safety for events.
2025-04-07 19:50:41 +02:00
e4a8c05d25 web/admin: fix diff showing previous false as "-" (#13580)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-03-19 23:06:37 +00:00
46a968d1dd web: Improve form input validation and visibility. (#12812) 2025-02-14 02:11:35 +01:00
656beebd63 web/components: ak-number-input: add support for min (#12703) 2025-01-20 17:29:44 +01:00
2893a54ffb web/admin: more cleanup and consistency (#12657)
* web/admin: migrate user interface and stop impersonation to nav bar

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

* move version diff to banner

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

* make click on backdrop close about modal

just for you @rissson

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2025-01-13 20:25:34 +01:00
d17f781d11 web: misc fixes for admin and flow inspector (#12461)
* fix flow inspector not closable on error

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

# Conflicts:
#	authentik/enterprise/providers/ssf/views/configuration.py

* unrelated: fix flow inspector for in memory stages

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

* only open inspector when there's size

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

* fix relative links

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-23 14:08:42 +01:00
dc3559c7e9 web: housekeeping, optimizations and small fixes (#12450)
* web/user: fix incorrect font in RAC endpoint popup

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

* fix navbar button colour in light mode

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

* add about modal

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

* fix sidebar overlapping page header

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

* fix wizard hint alignment

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

* add loading state to about modal

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

* add version context

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

* stub out init functions on loading interface

saves 4 HTTP requests on each full page load 🎉

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

* fix z-index for panels

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

* remove redundant api request

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-22 17:01:46 +01:00
02bd699917 web/admin: Refine navigation (#12441)
* fix spacing if there's no icon in page header

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

* add a very slight bar

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

* rework navigation to be similar between interfaces

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

* fix subpath and rendering

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

* fix display

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

* add version to sidebar

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

* make page header sticky?

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

* unrelated: hide session in system api

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

* unrelated: add unidecode for policies

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

#5859

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-12-21 22:12:47 +01:00
c528a6c336 web/admin: add application bindings to the application wizard (#11462)
* web: fix Flash of Unstructured Content while SearchSelect is loading from the backend

Provide an alternative, readonly, disabled, unindexed input object with the text "Loading...", to be
replaced with the _real_ input element after the content is loaded.

This provides the correct appearance and spacing so the content doesn't jiggle about between the
start of loading and the SearchSelect element being finalized.  It was visually distracting and
unappealing.

* web: comment on state management in API layer, move file to point to correct component under test.

* web: test for flash of unstructured content

- Add a unit test to ensure the "Loading..." element is displayed correctly before data arrives
- Demo how to mock a `fetchObjects()` call in testing. Very cool.
- Make distinguishing rule sets for code, tests, and scripts in nightmare mode
- In SearchSelect, Move the `styles()` declaration to the top of the class for consistency.

- To test for the FLOUC issue in SearchSelect.

This is both an exercise in mocking @beryju's `fetchObjects()` protocol, and shows how we can unit
test generic components that render API objects.

* web: interim commit of the basic sortable & selectable table.

* web: added basic unit testing to API-free tables

Mostly these tests assert that the table renders and that the content we give it
is where we expect it to be after sorting. For select tables, it also asserts that
the overall value of the table is what we expect it to be when we click on a
single row, or on the "select all" button.

* web: finalize testing for tables

Includes documentation updates and better tests for select-table.

* Provide unit test accessibility to Firefox and Safari; wrap calls to manipulate test DOMs directly in a browser.exec call so they run in the proper context and be await()ed properly

* web: repeat is needed to make sure sub-elements move around correctly. Map does not do full tracking.

* web: Update HorizontalLightComponent to accurately convey its value "upwards."

* interim commit, gods, the CSS is finally working.

* web: update

Got the binding editor in.  The tests complete.  Removed sonarjs.

* web: fixed tests to complete.

* web: fixed round-trip between binding list and binding editor. Fixed 'delete'.  TODO: Fix error reporting on home page, the edit button is ugly, and the height is off somehow, but I'm not yet sure how. I just know it bugs my eyes.

* core: add support to set policy bindings in transactional endpoint

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

* improve permission checks

especially since we'll be using the wizard as default in the future, it shouldn't be superuser only

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

* web: update api-less tables

- Replace `th` with `td` in `thead` components. Because Patternfly.
- Add @beryju's styling to the tables, which make it much better looking

* web: wizard for applications, now with bindings!

- Add policy bindings to the application wizard

- Restructures the Wizard base code.
  - ak-wizard-steps holds the steps and listens for NavigationRequest events to move
    from one step to the next.
  - WizardStep is a base class (no component registration provided) that provides the *whole frame*,
    not just the form.  It receives the navigation content for the sidebar from ak-wizard-steps,
    and provides the styling for the header, footer, sidebar, and main form.  It has abstractions
    for `buttons`, `renderMain()`, `handleButton()`, `handleEnable()`, in a section well-marked as
    "Public API".  Steps inherit from this class.

Conceptually:

- A wizard is a series of pages ("steps") with a distinct beginning and end, linked in a series,
  to complete a task.
- Later steps in the series are inaccessible until an earlier steps has granted access to it.
- Access is predicated on the earlier step being complete and valid. The developer is responsible
  for determining what "complete and valid" means.
- The series is visible, giving the customer a sense of how much effort is needed to complete the
  task.
- A parent object maintains (and can modify as needed) the list of steps. It *can* maintain the
  information being collected from the user. Alternatively, that information can be kept in each
  step.

Details:

- Keeping with the Lit paradigm, "requests to change the system flow up, information changed by
  valid requests flows down."
- The information flows up using events: WizardNavigation, WizardUpdate, WizardClose.
- The information flows down using properties.

- ak-application-wizard-main holds the list of steps, providing a unique slot name for each.
  - It maintains the ApplicationWizardState object.
- ApplicationWizardStep inherits from WizardStep and provides:
  - A means of extraction information from forms
  - A convenience method for updating the ApplicationWizardState object, enabling future steps, and
    navigating to a future step, in the correct order.
  - A method for cleaning error from the error reporting mechanism as the user navigates from an
    error-handling state.
  - The title, description, and cancelability of the wizard.
- Steps:
  - step: Handles the application. A good starting point for understanding the point of
    the Wizard.  Check the `handleButton()` method to understand how we enable or disable access to
    future steps.
  - provider-choice: Just a list. Shows validation without the form.
  - provider: Uses a *very* esoteric Lit feature, `unsafeStaticTag`, which enables
    the display to show anything that conforms to the expectations of ApplicationWizardProviderForm.
    - ApplicationWizardProviderForm repeats some of the base of ApplicationWizardStep, but allows us
      to provide multiple variants on a single form without having to create separate steps for each
      form.
    - The forms (`provider-for-ldap`, `provider-for-radius`) are therefore *just* the form and any
      fetchers needed to populate it.
  - bindings: Shows the table of bindings.  Has a custom display for "This table is empty."
  - edit-binding: Showcase for the `SearchSelectEZ` configuration format. Has an override on the
    `handleButton` feature to figure out which binding is about to be overridden. Is also a
    `.hidden` page; it doesn't show up on the navigation sidebar, as is only navigable-to by buttons
    not associated with the button bar at the bottom.
  - submit: Has a lot of machinery of state: Reviewing with errors, reviewing without errors,
    running submission, and success. Uses `ts-pattern` a lot to make sure the state/request pairs
    make sense.

The key insight is that, even though a wizard is a series in order, that order can't be simply
maintained in a list. The parent needs various strategies for swapping pages in and out of the
sequence, while still maintaining a coherent idea of "flow" and providing the visual cues the user
needs to feel confident that the work can be completed and completed quickly. The entire mechanism
for using an array and index to navigate, with index numbering, blocked the implementation of the
bindings pages.

One thing led to another.  *Sigh*  Really wish this hadn't been as much of a mess as it turned out.
The end result is pretty good, though.  Definitely re-usable.

One important feature to note is that the wizard is *not* tied to the ModalButton object; it's
simply embedded in a modal as-needed.  This allows us to use wizards in other places, such as just
being in a DIV, or just a page on its own.

* web: rollback dependabot "upgrade" that broke testing

Dependabot rolled us into WebdriverIO 9.  While that's probably the
right thing to do, right now it breaks out end-to-end tests badly.
Dependabot's mucking with infrastructure should not be taken lightly,
especially in cases when the infrastructure is for DX, not UX, and
doesn't create a bigger attack surface on the running product.

* web: small fixes for wdio and lint

- Roll back another dependabot breaking change, this time to WebdriverIO
- Remove the redundant scripts wrapping ESLint for Precommit mode. Access to those modes is
  available through the flags to the `./web/scripts/eslint.mjs` script.
- Remove SonarJS checks until SonarJS is ESLint 9 compatible.
- Minor nitpicking.

* web: not sure where all these getElement() additions come from; did I add them?  Anyway, they were breaking the tests, they're a Wdio9-ism.

* package-lock.json update

* web: small fixes for wdio and lint

**PLEASE** Stop trying to upgrade WebdriverIO following Dependabot's instructions. The changes
between wdio8 and wdio9 are extensive enough to require a lot more manual intervention. The unit
tests fail in wdio 9, with the testbed driver Wdio uses to compile content to push to the browser
([vite](https://vitejs.dev) complaining:

```
2024-09-27T15:30:03.672Z WARN @wdio/browser-runner:vite: warning: Unrecognized default export in file /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
  Plugin: postcss-lit
  File: /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
[0-6] 2024-09-27T15:30:04.083Z INFO webdriver: BIDI COMMAND script.callFunction {"functionDeclaration":"<Function[976 bytes]>","awaitPromise":true,"arguments":[],"target":{"context":"8E608E6D13E355DFFC28112C236B73AF"}}
[0-6]  Error:  Test failed due to following error(s):
  - ak-search-select.test.ts: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default': SyntaxError: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default'

```

So until we can figure out why the Vite installation isn't liking our CSS import scheme, we'll
have to soldier on with what we have.  At least with Wdio 8, we get:

```
Spec Files:      7 passed, 7 total (100% completed) in 00:00:19
```

* Forgot to run prettier.

* web: small fixes for elements and forms

- provides a new utility, `_isSlug_`, used to verify a user input
- extends the ak-horizontal-component wrapper to have a stronger identity and available value
- updates the types that use the wrapper to be typed more strongly
  - (Why) The above are used in the wizard to get and store values
- fixes a bug in SearchSelectEZ that broke the display if the user didn't supply a `groupBy` field.
- Adds `@wdio/types` to the package file so eslint is satisfied wdio builds correctly
- updates the end-to-end test to understand the revised button identities on the login page
  - Running the end-to-end tests verifies that changes to the components listed above did not break
    the semantics of those components.

* Prettier had opinions

* Fix the oauth2 provider test.

* web: fix oauth2 provider.  Fix resolutions in package-lock.json

* Provide an error field for the form errors on the OAuth2 form.  Unfortunately, this does not solve the general problem that we have a UX issue with which stage bindings to show where now that we've introduced the Invalidation Stage.

* 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.

* web/admin: provide default invalidation flows for LDAP provider.

* admin/web: the default invalidation flows for LDAP and Radius are different from the others.

* Updating the SAML Wizard page to correspond to the provider page.  *This is an intermediate fix to get the tests passing. It will probably be mooted with the next revision.*

* Making progress...

* web/admin: provider formectomy complete

* fix minor issues

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

* custom ordering for provider types

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

* fix css

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

* fix missing PFBase causing wrong font

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

* fix missing card for type select

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

* fix padding on last page

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

* add card to bindings

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

* web/element/wizard: fix the CSS cascade so the modifications to the title display don't affect the wiard header.

* web/elements/wizard: fix logic on unavailable / available / current indicators in nav bar.

* Debugging code is not needed.

* web: small visual fixes

As requested by reviewers:

- Fixed the height to 75% of the viewport
- Put 1rem of whitespace between the hint label and the Wizard startup button.

* web: disable lint check for cAsEfUnNy AtTrIbUtE nAmEs.

* Apply suggestions from code review

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>

* rework title

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

* format

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2024-12-18 18:44:27 +01:00
0a1d283ac8 web: provide storybook demos and docs for existing tests (#11651)
* Added tests and refinements as tests indicate.

* Building out the test suite.

* web: test the simple things. Fix what the tests revealed.

- Move `EmptyState.test.ts` into the `./tests` folder.
- Provide unit tests for:
  - Alert
  - Divider
  - Expand
  - Label
  - LoadingOverlay
- Give all tested items an Interface and a functional variant for rendering
- Give Label an alternative syntax for declaring alert levels
- Remove the slot name in LoadingOverlay
  - Change the slot call in `./enterprise/rac/index.ts` to not need the slot name as well
- Change the attribute names `topMost`, `textOpen`, and `textClosed` to `topmost`, `text-open`, and
  `text-closed`, respectively.
  - Change locations in the code where those are used to correspond

** Why interfaces: **

Provides another check on the input/output boundaries of our elements, gives Storybook and
WebdriverIO another validation to check, and guarantees any rendering functions cannot be passed
invalid property names.

** Why functions for rendering: **

Providing functions for rendering gets us one step closer to dynamically defining our forms-in-code
at runtime without losing any type safety.

** Why rename the attributes: **

A *very* subtle bug:
[Element:setAttribute()](https://developer.mozilla.org/en-US/docs/Web/API/Element/setAttribute)
automatically "converts an attribute name to all lower-case when called on an HTML element in an
HTML document." The three attributes renamed are all treated *as* attributes, either classic boolean
or stringly-typed attributes, and attempting to manipulate them with `setAttribute()` will fail.

All of these attributes are presentational; none of them end up in a transaction with the back-end,
so kebab-to-camel conversions are not a concern.

Also, ["topmost" is one word](https://www.merriam-webster.com/dictionary/topmost).

** Why remove the slot name: **

Because there was only one slot.  A name is not needed.

* Fix minor spelling error.

* First pass at a custom, styled input object.

* .

* web: Demo the simple things. Fix things the Demo says need fixing.

- Move the Element's stories into a `./stories` folder
- Provide stories for (these are the same ones "provided tests for" in the [previous
  PR](https://github.com/goauthentik/authentik/pull/11633))
  - Alert
  - Divider
  - Expand
  - Label
  - LoadingOverlay
- Provide Storybook documentation for:
  - AppIcon
  - ActionButton
  - AggregateCard
  - AggregatePromiseCard
  - QuickActionsCard
  - Alert
  - Divider
  - EmptyState
  - Expand
  - Label
  - LoadingOverlay
  - ApplicationEmptyState
- Fix a bug in LoadingOverlay; naming error in nested slots caused any message attached to the
  overlay to not sow up correctly.
- Revise AppIcon to be independent of authentik; it just cares if the data has a name or an icon
  reference, it does not need to know about `Application` objects. As such, it's an *element*, not a
  *component*, and I've moved it into the right location, and updated the few places it is used to
  match.

* Prettier has opinions with which I sometimes diverge.

* Found a bug! Although pf-m-xl was defined as a legal size, there was no code to handle drawing something XL!

* Found a few typos and incorrect API descriptions.
2024-10-14 10:30:09 -07:00
9e2620a5b9 web: provide simple tables for API-less displays (#11028)
* web: fix Flash of Unstructured Content while SearchSelect is loading from the backend

Provide an alternative, readonly, disabled, unindexed input object with the text "Loading...", to be
replaced with the _real_ input element after the content is loaded.

This provides the correct appearance and spacing so the content doesn't jiggle about between the
start of loading and the SearchSelect element being finalized.  It was visually distracting and
unappealing.

* web: comment on state management in API layer, move file to point to correct component under test.

* web: test for flash of unstructured content

- Add a unit test to ensure the "Loading..." element is displayed correctly before data arrives
- Demo how to mock a `fetchObjects()` call in testing. Very cool.
- Make distinguishing rule sets for code, tests, and scripts in nightmare mode
- In SearchSelect, Move the `styles()` declaration to the top of the class for consistency.

- To test for the FLOUC issue in SearchSelect.

This is both an exercise in mocking @beryju's `fetchObjects()` protocol, and shows how we can unit
test generic components that render API objects.

* web: interim commit of the basic sortable & selectable table.

* web: added basic unit testing to API-free tables

Mostly these tests assert that the table renders and that the content we give it
is where we expect it to be after sorting. For select tables, it also asserts that
the overall value of the table is what we expect it to be when we click on a
single row, or on the "select all" button.

* web: finalize testing for tables

Includes documentation updates and better tests for select-table.

* Provide unit test accessibility to Firefox and Safari; wrap calls to manipulate test DOMs directly in a browser.exec call so they run in the proper context and be await()ed properly

* web: repeat is needed to make sure sub-elements move around correctly. Map does not do full tracking.

* web: update api-less tables

- Replace `th` with `td` in `thead` components. Because Patternfly.
- Add @beryju's styling to the tables, which make it much better looking

* web: rollback dependabot "upgrade" that broke testing

Dependabot rolled us into WebdriverIO 9.  While that's probably the
right thing to do, right now it breaks out end-to-end tests badly.
Dependabot's mucking with infrastructure should not be taken lightly,
especially in cases when the infrastructure is for DX, not UX, and
doesn't create a bigger attack surface on the running product.

* web: small fixes for wdio and lint

- Roll back another dependabot breaking change, this time to WebdriverIO
- Remove the redundant scripts wrapping ESLint for Precommit mode. Access to those modes is
  available through the flags to the `./web/scripts/eslint.mjs` script.
- Remove SonarJS checks until SonarJS is ESLint 9 compatible.
- Minor nitpicking.

* web: not sure where all these getElement() additions come from; did I add them?  Anyway, they were breaking the tests, they're a Wdio9-ism.

* package-lock.json update

* web: small fixes for wdio and lint

**PLEASE** Stop trying to upgrade WebdriverIO following Dependabot's instructions. The changes
between wdio8 and wdio9 are extensive enough to require a lot more manual intervention. The unit
tests fail in wdio 9, with the testbed driver Wdio uses to compile content to push to the browser
([vite](https://vitejs.dev) complaining:

```
2024-09-27T15:30:03.672Z WARN @wdio/browser-runner:vite: warning: Unrecognized default export in file /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
  Plugin: postcss-lit
  File: /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
[0-6] 2024-09-27T15:30:04.083Z INFO webdriver: BIDI COMMAND script.callFunction {"functionDeclaration":"<Function[976 bytes]>","awaitPromise":true,"arguments":[],"target":{"context":"8E608E6D13E355DFFC28112C236B73AF"}}
[0-6]  Error:  Test failed due to following error(s):
  - ak-search-select.test.ts: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default': SyntaxError: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default'

```

So until we can figure out why the Vite installation isn't liking our CSS import scheme, we'll
have to soldier on with what we have.  At least with Wdio 8, we get:

```
Spec Files:      7 passed, 7 total (100% completed) in 00:00:19
```

* Forgot to run prettier.

* web: small fixes for elements and forms

- provides a new utility, `_isSlug_`, used to verify a user input
- extends the ak-horizontal-component wrapper to have a stronger identity and available value
- updates the types that use the wrapper to be typed more strongly
  - (Why) The above are used in the wizard to get and store values
- fixes a bug in SearchSelectEZ that broke the display if the user didn't supply a `groupBy` field.
- Adds `@wdio/types` to the package file so eslint is satisfied wdio builds correctly
- updates the end-to-end test to understand the revised button identities on the login page
  - Running the end-to-end tests verifies that changes to the components listed above did not break
    the semantics of those components.

* Prettier had opinions

* Some lint over-eagerness.

* Updated after build.
2024-10-07 08:21:13 -07:00
5257370e4a web: small fixes for elements and forms (#11546)
* web: small fixes for wdio and lint

- Roll back another dependabot breaking change, this time to WebdriverIO
- Remove the redundant scripts wrapping ESLint for Precommit mode. Access to those modes is
  available through the flags to the `./web/scripts/eslint.mjs` script.
- Remove SonarJS checks until SonarJS is ESLint 9 compatible.
- Minor nitpicking.

* package-lock.json update

* web: small fixes for wdio and lint

**PLEASE** Stop trying to upgrade WebdriverIO following Dependabot's instructions. The changes
between wdio8 and wdio9 are extensive enough to require a lot more manual intervention. The unit
tests fail in wdio 9, with the testbed driver Wdio uses to compile content to push to the browser
([vite](https://vitejs.dev) complaining:

```
2024-09-27T15:30:03.672Z WARN @wdio/browser-runner:vite: warning: Unrecognized default export in file /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
  Plugin: postcss-lit
  File: /Users/ken/projects/dev/web/node_modules/@patternfly/patternfly/components/Dropdown/dropdown.css
[0-6] 2024-09-27T15:30:04.083Z INFO webdriver: BIDI COMMAND script.callFunction {"functionDeclaration":"<Function[976 bytes]>","awaitPromise":true,"arguments":[],"target":{"context":"8E608E6D13E355DFFC28112C236B73AF"}}
[0-6]  Error:  Test failed due to following error(s):
  - ak-search-select.test.ts: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default': SyntaxError: The requested module '/src/common/styles/authentik.css' does not provide an export named 'default'

```

So until we can figure out why the Vite installation isn't liking our CSS import scheme, we'll
have to soldier on with what we have.  At least with Wdio 8, we get:

```
Spec Files:      7 passed, 7 total (100% completed) in 00:00:19
```

* Forgot to run prettier.

* web: small fixes for elements and forms

- provides a new utility, `_isSlug_`, used to verify a user input
- extends the ak-horizontal-component wrapper to have a stronger identity and available value
- updates the types that use the wrapper to be typed more strongly
  - (Why) The above are used in the wizard to get and store values
- fixes a bug in SearchSelectEZ that broke the display if the user didn't supply a `groupBy` field.
- Adds `@wdio/types` to the package file so eslint is satisfied wdio builds correctly
- updates the end-to-end test to understand the revised button identities on the login page
  - Running the end-to-end tests verifies that changes to the components listed above did not break
    the semantics of those components.

* Removing SonarJS comments.

* Reverting to log level  for tests.
2024-10-03 14:56:28 -07:00
c90ecf4ebe admin: refactor update check (#11272)
* admin: refactor update check

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

* fix tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-09-08 01:35:07 +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
453f7b8641 web: provide default endpoint api configuration (#10319)
* 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.

* Intermediate; prepping for remove that may fail.

* web: provide a default table endpoint configuration

This commit finds 19 places where the exact same configuration is
used to describe a table's API endpoint, and replaces that configuration
with a provided default from a parent class.

While examining the logs for our build, I noted that this particular
sequence is duplicated multiple times throughout our code base,
accounting for a bloat of 169 lines or so of the estimated 5552
lines of bloat.  By providing a default endpoint configuration and
substituting it (mechanically) wherever the default is required,
we reduce our code duplication issue from 9.26% of the codesabe
to 8.99%.

... which is a start.

* Didn't need the duplication.

* remove page argument while we're at it

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

* actually use it everywhere

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

* web: fix inconsistent method signature for LogViewer

Removed the `_page` parameter from LogViewer's apiEndpoint() method.

The `page: number` parameter is no longer a part of this method's signature.

* web: restore reduced page size to Overview:Recent Events card

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-07-02 14:55:29 +02:00
6c4c535d57 web/admin: rework initial wizard pages and add grid layout (#9668)
* remove @goauthentik/authentik as TS path

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

* initial implementation

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

* oh yeah

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

* format earlier changes

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

* support plain alert

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

* initial attempt at dedupe

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

* make it a base class

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

* migrate all wizards

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

* create type create mixin to dedupe more, add icon to source create

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

* add ldap icon

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

* Optimised images with calibre/image-actions

* match inverting

we should probably replace all icons with coloured ones so we don't need to invert them...I guess

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

* format

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

* make everything more explicit

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

* add icons to provider

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

* add remaining provider icons

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

* rework to not use inheritance

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

* fix unrelated typo

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

* make app wizard use grid layout

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

* keep wizard height consistent

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

* fix tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
2024-05-22 02:41:33 +02:00
a60442fc2c enterprise/audit: fix audit logging with m2m relations (#9571) 2024-05-05 02:33:38 +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
2ba66f4f91 web: upgrade to lit 3 (#8781)
* Holding for a moment...

* web: replace rollup with esbuild

This commit replaces rollup with esbuild.

The biggest fix was to alter the way CSS is imported into our system;
esbuild delivers it to the browser as text, rather than as a bundle
with metadata that, frankly, we never use.  ESBuild will bundle the
CSS for us just fine, and interpreting those strings *as* CSS turned
out to be a small hurdle.  Code has been added to AKElement and
Interface to ensure that all CSS referenced by an element has been
converted to a Browser CSSStyleSheet before being presented to the
browser.

A similar fix has been provided for the markdown imports.  The
biggest headache there was that the re-arrangement of our documentation
broke Jen's existing parser for fixing relative links.  I've provided
a corresponding hack that provides the necessary detail, but since
the Markdown is being presented to the browser as text, we have to
provide a hint in the markdown component for where any relative
links should go, and we're importing and processing the markdown
at runtime.  This doesn't seem to be a big performance hit.

The entire build process is driven by the new build script, `build.mjs`,
which starts the esbuild process as a service connected to the build
script and then runs the commands sent to it as fast as possible.
The biggest "hack" in it is actually the replacement for rollup's
`rollup-copy-plugin`, which is clever enough I'm surprised it doesn't
exist as a standalone file-copy package in its own right.

I've also used a filesystem watch library to encode a "watcher"
mechanism into the build script.  `node build.mjs --watch` will
work on MacOS; I haven't tested it elsewhere, at least not yet.

`node build.mjs --proxy` does what the old rollup.proxy.js script
did.

The savings are substantial.  It takes less than two seconds to build
the whole UI, a huge savings off the older ~45-50 seconds I routinely
saw on my old Mac.  It's also about 9% smaller.

The trade-offs appear to be small: processing the CSS as StyleSheets,
and the Markdown as HTML, at run-time is a small performance hit,
but I didn't notice it in amongst everything else the UI does as
it starts up.

Manual chunking is gone; esbuild's support for that is quite difficult
to get right compared to Rollup's, although there's been a bit of
yelling at ESbuild over it.  Codemirror is built into its own chunk;
it's just not _named_ distinctly anymore.

The one thing I haven't been able to test yet is whether or not the
polyfills and runtim shims work as expected on older browsers.

* web: continue with performance and build fixes

This commit introduces a couple of fixes enabled by esbuild and other
features.

1. build-locales

`build-locales` is a new NodeJS script in the `./scripts` folder
that does pretty much what it says in the name: it translates Xliff
files into `.ts` files.  It has two DevExp advantages over the old
build system.

First, it will check the build times of the xlf files and
their ts equivalents, and will only run the actual build-locales
command if the XLF files are newer than their TS equivalents.

Second, it captures the stderr output from the build-locales command
and summarizes it.  Instead of the thousands of lines of "this
string has no translation equivalent," now it just reports the
number of missed translations per locale.

2. check-spelling

This is a simple wrapper around the `codespell` command, mostly
just to reduce the visual clutter of `package.json`, but also to
permit it to run just about anywhere without needed hard-coded
paths to the dictionaries, using a fairly classic trick with git.

3. pseudolocalize and import-maps

These scripts were in TypeScript, but for our purposes I've
saved their constructed equivalents instead.  This saves on
visual clutter in the `package.json` script, and reduced the
time they have to run during full builds.  They're small enough
I feel confident they won't need too much looking over.

Also, two lint bugs in Markdown.ts have been fixed.

* Removed a few lines that weren't in use.

* build-locales was sufficiently complex it needed some comments.

* web: formalize that horrible unixy git status checker into a proper function.

* Added types for , the Markdown processor for in-line documentation.

* web: upgrade to Lit3

This commit replaces our Lit2 implementation with a Lit3 implementation.

This upgrade required two major shifts within our code, both of them consequential.

First, the restructuring of the way the get/set decorators for properties and states meant that a
lot of the code we were using needed to be refactored. More than that, a lot of those custom
accessors were implemented to trigger side-effects, such as when a providerID is set or changed
triggering the ProviderView to fetch the requsted Provider. The Lit2 and Lit3 documentation both say
[there is a better way to handle
this](https://lit.dev/docs/v2/components/properties/#:~:text=In%20most%20cases%2C%20you%20do%20not%20need%20to%20create%20custom%20property%20accessors)
by detecting the change in the `willUpdate()` point of an elements Lifecycle and triggering the side
effect there instead. I've done this in several places with a pattern of detecting the change, and
then naming the corresponding change as `fetchRequestedThing()`. The resulting code is cleaner and
uses fewer controversial features.

The other is that the type signature for `LitElement.createRenderRoot()` has changed to be either an
HTMLElement or a DocumentFragment. This required some serious refactoring of type changes through
Base and Interface codes. Noteably, the custom `AdoptedStyleSheetsElement` interface has been
superseded by the supplied and standardized
[DocumentOrShadowRoot](aa2b2352e1/src/lib/dom.generated.d.ts (L4715))
interface. Unfortunately, that interface is a mixin, and casting or instance checking are still in
place to make sure the objects being manipulated are typed "correctly."

Three files I touched during the course of this triggered SonarJS, so there are some minor fixes,
replacing some awkward syntax with more idiomatic code.  These are very minor, such as replacing:

```
const result = someFunction();
return result;

/* with */

return someFunction();

```

and

```
const result = x();
if (!result) { return true } else { return false }

/* with */

return !x();

```

* fix package lock

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

* don't use hardcoded magic values

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-03-11 17:47:57 +00:00
0c4dee8f9f providers: allow previewing mappings for other users (#8297)
* rework access check to do better validation

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

* providers: allow previewing mappings for other users

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

* fix ui

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

* Revert "rework access check to do better validation"

This reverts commit 81077a7e7b.

* prepare

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-01-29 13:58:51 +01:00
11ca358242 web/admin: revamped rbac and user settings tabs (#8299)
* web/admin: fix duplicate RBAC preview banner on permission modal

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

* switch non-embedded permission page to use vertical tabs

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

* fix some leftover html?

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

* move stuff into vertical subtab

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

* show all of users permission tabs on one main tab

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

* rework role page to match user page

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

* use separate tabs

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

* rename role permission tables to match user tables

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

* rename to credentials and tokens

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

* add country icon to session list

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

* add oauth access token list

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

* add helper to get relative time

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

* use pfdivider

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

* replace plain hr with pf-c-divider

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

* use new logic for showing relative time in charts

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

* use consistent relative time for event display

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

* remove more leftovers

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

* fix some alignment issues on the admin dashboard

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

* update storybook map

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

* add sanity check to event app lookup

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

* make api drawer header fixed

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

* fix table padding for toggle

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

* fix notification drawer for user interface

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

* enable system task search

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

* fix formatting, exclude generated script from formatting

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

* web: minor fixes

There's a renderer (it's not a component, not yet) for producing definition lists without
the risk of missing a class or tag.

Breaking conditionally rendered components out to make their use easier to identify.

* fix prettier

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

* fix outpost form

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

* fix more flaky tests

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

* re-create locale

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

* add some description for different permission views

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

* fix system task search

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

* update docs

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
2024-01-26 18:01:03 +01:00
96b2a1a9ba events: migrate SystemTasks to DB (#8159)
* events: migrate system tasks to save in DB

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

* prefill in app startup

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

* cleanup api

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

* update web

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

* use string for status

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

* fix enum

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

* save start and end directly in timestamp from default_timer()

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

* improve metrics

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

* lint

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

* fix tests

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

* rename globally to system task

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

* recreate migrations, better denote anonymous user

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

* events: lookup actual django app instead of using module path, fallback to module path

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

* fix logger call

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-01-24 17:23:03 +01:00
4184f8a770 enterprise: add full audit log [AUTH-458] (#8177)
* enterprise: add full audit log

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

* delegate enabled check to apps

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

* move audit middleware to separate app

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

* cleanse before diff

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

* make cleanse include a hash of the values

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

* fix sentry error during lint

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

* format

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

* fix tests?

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

* only use start of hash

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

* don't use deepdiff

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

* add diff ui

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

* fix info for dict

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

* update release notes

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

* enable audit logging for tests

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

* fix startup with tests

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

* lint

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

* include first 4 chars of raw value?

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

* only log asterisks

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

* fixup

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

* fix tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2024-01-24 11:36:06 +01:00
830689f1cb web: bad default in select (#8258)
* web: fix event propogation in search-select wrappers

Two different patches, an older one that extracted long search
blocks that were cut-and-pasted into a standalone component, and a
newer one that fixed displaying placeholder values properly,
conflicted and broke a relationship that allowed for the values to
be propagated through those standalone components correctly.

This restores the event handling and updates the listener set-ups
with more idiomatic hooks into Lit's event system.

* Updated search-select to properly render with Storybook, and provided a
foundation for testing the Search-Select component with Storybook.

* Accidentally deleted this line while making Sonar accept my test data.

* Fixing a small issue that's bugged me for awhile: there's no reason to manually duplicate what code can duplicate.

* Provided a storybook for testing out the flow search.

Discovered along the way that I'd mis-used a prop-drilling technique which caused the currentFlow
to be "undefined" when pass forward, giving rise to Marc's bug.

I *think* this shakes out the last of the bugs.  Events are passed up correctly and the initial value
is recorded correctly.

* Added comments and prettier had opinions.

* Restoring old variable names; they didn't have to change after all.

* fix lint

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2024-01-23 08:54:34 -08:00
240cf6dd94 enterprise/providers: Add RAC [AUTH-15] (#7291)
* add basic guacamole

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

* make everything mostly work

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

* add rac build to CI

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

* fix resize, fix web lint, sendSize correctly

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

* pre-send connection from client, format

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

* improve throughput

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

* cleanup

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

* rework TokenOutpostConsumer into middleware

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

* fix some layout issues

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

* add outpost controllers

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

* start testing audio things

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

* fix a bunch of things

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

* add deps

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

* fix to work with outpost group

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

* add simple loadbalancing

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

* add simple reconnect

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

* show reconnecting text

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

* fix error when checking ports

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

* move to providers

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

* add flow check to interface

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

* fix go lint

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

* fix rac app label

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

* fix audio

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

* add logging

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

* cleanup

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

* allow overriding all settings

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

* fix duplicate keyboard, debug high DPI

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

* re-add deps

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

* fix lint

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

* fix missing __init__.py breaking model loading

I love python

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

* fix tests

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

* bump successful ws connection to info

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

* hide cursor since guac draws that

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

* add clipboard support (bidirectional)

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

* make codespell not want to break the code

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

* run pr comment in separate task

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

* start endpoint and property mapping stuff

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

* more endpoint things

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

* unrelated: fix event model_pk filtering with ints

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

* unrelated: improve event display for changelog

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

* rebuild endpoint stuff again

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

* idk special url

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

* more stuff, connect token with session

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

* add disconnect

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

* rework disconnect

cleanly disconnect from guacd instead of just letting the connection timeout

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

* clear cache when creating outpost

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

* support host:port and fix protocol

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

* center smaller viewport

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

* rework connection to wait more and stop after some time

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

* add policy control to endpoints

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

* remove provider protocol

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

* don't switch to different outpost connection when already chosen

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

* start using property mappings, add static settings

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

* add some RAC mapping settings

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

* fix lint

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

* start adding tests

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

* add tests for event changes

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

* add tests and fix issues found by said tests

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

* add preview banner, move endpoints to main page

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

* add locale

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

* auto-select endpoint if only one is available

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

* backport https://github.com/goauthentik/authentik/pull/7831 to rac

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

* dont select property mappings on endpoints

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

* make table modal only load when opened

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

* only auto-redirect when open

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

* fix web deps

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

* check for token expiry and terminate session

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

* re-add endpoint name to title

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

* disconnect connection when token is manually deleted

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

* add initial RAC docs

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

* add connection expiry setting to provider

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

* fix flaky tests

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-12-30 21:33:14 +01:00
ec8f2d4bf9 stages/email: prevent authentik emails from being marked as spam (also add text template support) (#7949)
* use <> style email address with name

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

* add support for text templates

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

* fix icon display in event log

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

* add text email templates

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

* update docs, update email screenshot

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

* prevent prettier from breaking example template

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

* Optimised images with calibre/image-actions

* Apply suggestions from code review

Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Signed-off-by: Jens L. <jens@beryju.org>

* reword docs

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Signed-off-by: Jens L. <jens@beryju.org>
Co-authored-by: authentik-automation[bot] <135050075+authentik-automation[bot]@users.noreply.github.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
2023-12-21 14:32:05 +01:00
026e80bd10 web: replace 'description-list' with list of descriptions (#7392)
* 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: description lists as functions

One thing I hate is clutter.  Just tell me what you're going to do.  "Description Lists" in our code are
renderings of Patternfly's DescriptionList; we use only four of
their idioms: horizontal, compact, 2col, and 3col.  With that in mind, I've stripped out the DescriptionList
rendering code from UserViewPage and replaced it with a list of "Here's what to render" and a function call
to render them.  The calling code is still responsible for having the right styles available, as this is
not a component or an attempt at isolation; it is *just* a function (at this point).

* web: fix issue that prevented the classMap from being rendered properly

* web: added comments to the description list.

* web: analyze & prettier had opinions

* web: Fix description-list demo

This commit re-instals the demo for the "description list" of user fields.

* web: prettier had opinions.

* any -> unknown

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-12-13 16:16:57 +01: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
73751e5cd9 web: refactor status label to separate component (#7407)
* 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.

* A quality of life thing: `<ak-status-label good>`

There's an idiom throughout the UI:

``` HTML
<ak-label color=${item.enabled ? PFColor.Green : PFColor.Red}>
      ${item.enabled ? msg("Yes") : msg("No")}
      </ak-label>
```

There are two problems with this.

- Repeating the conditional multiple times is error-prone
- The color scheme doesn't communicate much.

There are uses for ak-label that aren't like this, but I'm focusing on this particular use case,
which occurs about 20 times throughout the UI.

Since it's so common, let's isolate the most common case: `<ak-status-label good />` gives you the
"good" status, and `<ak-status-label/>` gives you the "bad" status, which is the default (no
arguments to the function).

There wasn't much clarity in the system for when to use orange vs red vs grey, but looking through
the use cases, it became clear that Red meant fail/inaccessible, Orange meant "Warning, but not
blocking," and Grey just means "info: this thing is off".

So let's define that with meaning: there are three types, error, warning, and info. Which
corresponds to debugging levels, but whatever, nerds grok that stuff.

So that example at the top becomes

```<ak-status-label ?good=${item.enabled}></ak-status-label>```

... and we can now more clearly understand what that conveys.

There is some heavy tension in this case: this is an easier and quicker-to-write solution to
informing the user of a binary status in an iconic way, but the developer has to remember that it
exists.

Story provided, and changes to the existing uses of the existing idiom provided.

* Added the 'compact label' story to storybook.
2023-11-20 11:24:48 -08:00