authentik fork
This repository has been archived on 2024-05-31. You can view files and clone it, but cannot push or open issues or pull requests.
Find a file
Ken Sternberg 3f02534eb1
web: weightloss program, part 1: FlowSearch (#6332)
* web: weightloss program, part 1: FlowSearch

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

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

... to:

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

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

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

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

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

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

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

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

Imagine, instead, if we could write:

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

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

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

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

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

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

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

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

And then `ak-form` itself has this:

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

This ought to read:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

* web: another codespell fix for a comment.

* web: adding 'codespell' to the pre-commit command. Fixed spelling error in eventEmitter.
2023-07-28 22:57:14 +02:00
.github translate: Updates for file locale/en/LC_MESSAGES/django.po in fr on branch main (#6376) 2023-07-26 11:03:37 +02:00
.vscode root: celery refactor (#6095) 2023-06-28 16:44:50 +02:00
authentik api: re-fix url import logging (#6400) 2023-07-27 12:56:51 +02:00
blueprints api: re-fix url import logging (#6400) 2023-07-27 12:56:51 +02:00
cmd cmd: use live endpoint instead of ready for inbuild healthcheck 2023-05-08 22:04:18 +02:00
internal outposts: make metrics compliant with Prometheus best-practices (#6398) 2023-07-27 18:51:08 +02:00
lifecycle root: partial Live-updating config (#5959) 2023-07-19 23:13:22 +02:00
locale web: app icons v2 (#6410) 2023-07-28 14:25:56 +02:00
schemas providers: SCIM (#4835) 2023-03-06 19:39:08 +01:00
scripts root: fix generate_config script not setting debug (#5465) 2023-05-03 23:52:59 +03:00
tests outposts: support json patch for Kubernetes (#6319) 2023-07-22 02:29:28 +02:00
web web: weightloss program, part 1: FlowSearch (#6332) 2023-07-28 22:57:14 +02:00
website website/blogs: this weeks blog (#6415) 2023-07-28 13:36:11 -07:00
.bumpversion.cfg release: 2023.6.1 2023-07-10 13:20:22 +02:00
.dockerignore enterprise: initial license (#5293) 2023-04-19 16:13:45 +02:00
.editorconfig website: codespell with custom dictionary and CI (#5062) 2023-03-24 00:24:55 +01:00
.gitignore enterprise: initial enterprise (#5721) 2023-07-17 17:57:08 +02:00
CODE_OF_CONDUCT.md root: rework and expand security policy 2022-11-28 12:10:53 +01:00
CODEOWNERS root: Test codeowners (#5586) 2023-05-11 20:26:04 +02:00
CONTRIBUTING.md website/developer-docs: move contributing to dev docs index and link contributing file (#5554) 2023-05-09 20:26:55 +02:00
docker-compose.yml release: 2023.6.1 2023-07-10 13:20:22 +02:00
Dockerfile core: bump maxmind/geoipupdate from v5.1 to v6.0 (#6240) 2023-07-13 12:51:37 +02:00
go.mod core: bump goauthentik.io/api/v3 from 3.2023061.5 to 3.2023061.6 (#6407) 2023-07-28 11:08:57 +02:00
go.sum core: bump goauthentik.io/api/v3 from 3.2023061.5 to 3.2023061.6 (#6407) 2023-07-28 11:08:57 +02:00
ldap.Dockerfile core: bump golang from 1.20.5-bullseye to 1.20.6-bullseye (#6226) 2023-07-12 11:48:11 +02:00
LICENSE enterprise: initial license (#5293) 2023-04-19 16:13:45 +02:00
Makefile web: basic cleanup of buttons (#6107) 2023-07-18 08:29:42 -07:00
manage.py root: update deprecation warnings 2022-11-25 11:47:28 +01:00
poetry.lock core: bump pylint from 2.17.4 to 2.17.5 (#6396) 2023-07-27 11:29:52 +02:00
proxy.Dockerfile core: bump golang from 1.20.5-bullseye to 1.20.6-bullseye (#6226) 2023-07-12 11:48:11 +02:00
pyproject.toml outposts: support json patch for Kubernetes (#6319) 2023-07-22 02:29:28 +02:00
radius.Dockerfile core: bump golang from 1.20.5-bullseye to 1.20.6-bullseye (#6226) 2023-07-12 11:48:11 +02:00
README.md web: Add storybook (#5865) 2023-06-07 13:05:33 +02:00
schema.yml api: re-fix url import logging (#6400) 2023-07-27 12:56:51 +02:00
SECURITY.md root: Update security policy (#6222) 2023-07-12 06:56:37 -06:00

authentik logo


Join Discord GitHub Workflow Status GitHub Workflow Status GitHub Workflow Status Code Coverage Docker pulls Latest version

What is authentik?

authentik is an open-source Identity Provider that emphasizes flexibility and versatility. It can be seamlessly integrated into existing environments to support new protocols. authentik is also a great solution for implementing sign-up, recovery, and other similar features in your application, saving you the hassle of dealing with them.

Installation

For small/test setups it is recommended to use Docker Compose; refer to the documentation.

For bigger setups, there is a Helm Chart here. This is documented here.

Screenshots

Light Dark

Development

See Developer Documentation

Security

See SECURITY.md

Adoption and Contributions

Your organization uses authentik? We'd love to add your logo to the readme and our website! Email us @ hello@goauthentik.io or open a GitHub Issue/PR! For more information on how to contribute to authentik, please refer to our CONTRIBUTING.md file.

Sponsors

This project is proudly sponsored by:

DigitalOcean provides development and testing resources for authentik.