Commit Graph

48 Commits

Author SHA1 Message Date
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
f728bbb14b sources/ldap: add check command to verify ldap connectivity (#7263)
* sources/ldap: add check command to verify ldap connectivity

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

* default to checking all sources

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

* start adding an API for ldap connectivity

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

* add webui for ldap source connection status

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

* better show sync status, clear previous tasks

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

* set timeout on redis lock for ldap sync

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

* fix py lint

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

* fix web lint

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-11-13 15:01:40 +01:00
20e003656a web: fix bad comment that was confusing lit-analyze (#7234)
The old comment was left over from a previous revision of the wizard, and
blocked lit-analyze's ability to understand the Modal's `slot="trigger"`
declaration.
2023-10-20 11:17:24 +02:00
3a7283c670 web: Application wizard v2 with tests (#7004)
* A lot of comments about forms.

* Adding comments to the wizard.

* Broke out the text input into a single renderer.  Still works as required.

* web: Legibility in the ApplicationForm.

This is a pretty good result.  By using the LightDOM setting, this
provides the existing Authentik form manager with access to the
ak-form-horizontal-element components without having to do any
cross-border magic.  It's not ideal, and it shows up just how badly
we've got patternfly splattered everywhere, but the actual results
are remarkable.  The patterns for text, switch, radio, textarea,
file, and even select are smaller and easier here.

I'm still noodling on what an unspread search-select element would
look like.  It's just dependency injection, so it ought to be as
straightforward as that.

* web: Marking down the start of the 'components' library.

* web: Baby steps

I become frustrated with my inability to make any progress on this project, so I decided to reach
for a tool that I consider highly reliable but also incredibly time-consuming and boring: test
driven development.

In this case, I wrote a story about how I wanted to see the first page rendered: just put the HTML
tag, completely unadorned, that will handle the first page of the wizard. Then, add an event handler
that will send the updated content to some parent object, since what we really want is to
orchestrate the state of the user's input with a centralized location. Then, rather than fiddling
with the attributes and properties of the various pages, I wanted them to be able to "look up" the
values they want, much as we'd expect a standalone form to be able to pull its values from the
server, so I added a context object that receives the update event and incorporates the new
knowledge about the state of the process into itself.

The result is surprisingly satisfying: the first page renders cleanly, displays the content that we
want, and as we fiddle with, we can *watch in real time* as the results of the context are updated
and retransmitted to all receiving objects. And the sending object gets the results so it
re-renders, but it ends up looking the same as it was before the render.

* Now, it's starting to look like a complete package. The LDAP method is working, but there is a bug:
the radio is sending the wrong value !?!?!?. Track that down, dammit. The search wrappers now resend
their events as standard `input` events, and that actually seems to work well; the browser is
decorating it with the right target, with the right `name` attribute, and since we have good
definitions of the `value` as a string (the real value of any search object is its UUID4), that
works quite well. Added search wrappers for CoreGroup and CryptoCertificate (CertificateKeyPairs),
and the latter has flags for "use the first one if it's the only one" and "allow the display of
keyless certificates."

Not sure why `state()` is blocking the transmission of typing information from the typed element
to the context handler, but it's a bug in the typechecker, and it's not a problem so far.

* Now, it's starting to look like a complete package. The LDAP method is working, but there is a bug:
the radio is sending the wrong value !?!?!?. Track that down, dammit. The search wrappers now resend
their events as standard `input` events, and that actually seems to work well; the browser is
decorating it with the right target, with the right `name` attribute, and since we have good
definitions of the `value` as a string (the real value of any search object is its UUID4), that
works quite well. Added search wrappers for CoreGroup and CryptoCertificate (CertificateKeyPairs),
and the latter has flags for "use the first one if it's the only one" and "allow the display of
keyless certificates."

Not sure why `state()` is blocking the transmission of typing information from the typed element
to the context handler, but it's a bug in the typechecker, and it's not a problem so far.

* web: tracked down that weirld bug with the radio.

Because radio inputs are actually multiples, the events handling for
radio is... wonky.  If we want our `<ak-radio>` component to be a
unitary event dispatcher, saying "This is the element selected," we
needed to do more than what was currently being handled.

I've intercepted the events that we care about and have placed
them into a controller that dictates both the setting and the
re-render of the component.  This makes it "controlled" (to use the
Angular/React/Vue) language and depends on Lit's reactiveElement
lifecycle to work, rather than trust the browser, but the browser's
experience with respect to the `<input type=radio` is pretty bad:
both input elements fire events, one for "losing selection" and
one for "gaining selection".  That can be very confusing to handle,
so we funnel them down in our aggregate radio element to a single
event, "selection changed".

As a quality-of-life measure, I've also set the label to be
unselectable; this means that a click on the label will trigger the
selection event, and a long click will not disable selection or
confuse the selection event generator.

* web: now passing the precommit phase

* web: a HACK for Storybook to inject the "use light theme" flag into the body.

This isn't really a very good hack; what it does is say that every story is
responsible for hacking its theme into the parent.  This is very annoying, but
it does mean that we can at least show our components in the best light.

* web: ak-application-wizard-authentication-by-oauth, and many fixes!

1. Fixed `eventEmitter` so that if the detail object is a scalar, it will not attempt to "objectify"
   it.  This was causing a bug where retrofitting the eventEmitter to some older components resulted
   in a detail of "some" being translated into ['s', 'o', 'm', 'e'].  Not what is wanted.
2. Removed the "transitional form" from the existing components; they had a two-step where the web
   component class was just a wrapper around an independent rendering function.  While this worked,
   it was only to make the case that they *were* independent rendering objects and could be
   supported with the right web component framework.  We're halfway there now; the last step will be
   to transform the horizontal-element and various input CSS into componentized CSS, the way
   Patternfly-Elements is currently doing.
3. Fixed the `help` field so that it could take a string or a TemplateResult, and if the latter,
   don't bother wrapping it in the helper text functionality; just let it be its own thing.  This
   supports the multi-line help of redirectURI as well as the `ak-utils-time-delta` capability.
4. Transform Oauth2ProviderForm to use the new components, to the best of our ability.  Also used
   the `provider = this.wizard.provider` and `provider = this.instance` syntax to make the render
   function *completely portable*; it's the exact same text that is dropped into...
5. The complete `ak-application-wizard-authentication-by-oauth` component. They're so similar part
   of me wonders if I could push them both out to a common reference, or a collection of common
   references.  Both components use the PropertyMapping and Sources, and both use the same
   collection of searches (Crypto, Flow).
6. A Storybook for `ak-application-wizard-authentication-by-oauth`, showing the works working.
7. New mocks for `authorizationFlow`, `propertyMappings`, and `hasJWKs`.

This sequence has revealed a bug in the radio control.  (It's always the radio control.)  If the
default doesn't match the current setting, the radio control doesn't behave as expected; it won't
change when you fully expect that it should.  I'll investigate how to harmonize those tomorrow.

* web: Converted our toggle groups to a more streamlined implementation.

* web: one more toggle group.

* initial api and schema

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

* separate blueprint importer from yaml parsing

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

* cleanup

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

* web: Replace ad-hoc toggle control with ak-toggle-group

This commit replaces various ad-hoc implementations of the Patternfly Toggle Group HTML with a web
component that encapsulates all of the needed behavior and exposes a single API with a single event
handler, return the value of the option clicked.

The results are: Lots of visual clutter is eliminated.  A single link of:

```
<div class="pf-c-toggle-group__item">
  <button
      class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy
          ? "pf-m-selected"
          : ""}"
      type="button"
      @click=${() => {
          this.mode = ProxyMode.Proxy;
      }}>
      <span class="pf-c-toggle-group__text">${msg("Proxy")}</span>
  </button>
</div>
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
```

Now looks like:

```
<option value=${ProxyMode.Proxy}>${msg("Proxy")}</option>
```

This also means that the three pages that used the Patternfly Toggle Group could eliminate all of
their Patternfly PFToggleGroup needs, as well as the `justify-content: center` extension, which also
eliminated the `css` import.

The savings aren't as spectacular as I'd hoped: removed 178 lines, but added 123; total savings 55
lines of code.  I still count this a win: we need never write another toggle component again, and
any bugs, extensions or features we may want to add can be centralized or forked without risking the
whole edifice.

* web: minor code formatting issue.

* add new "must_created" state to blueprints to prevent overwriting objects

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

* web: adding a storybook for the ak-toggle-group component

* Bugs found by CI/CD.

* web: Replace ad-hoc search for CryptoCertificateKeyPairs with ak-crypto-certeficate-search

This commit replaces various ad-hoc implementations of `search-select` for CryptoCertificateKeyPairs
with a web component that encapsulates all of the needed behavior and exposes a single API.

The results are: Lots of visual clutter is eliminated.  A single search of:

```HTML
<ak-search-select
    .fetchObjects=${async (query?: string): Promise<CertificateKeyPair[]> => {
        const args: CryptoCertificatekeypairsListRequest = {
            ordering: "name",
            hasKey: true,
            includeDetails: false,
        };
        if (query !== undefined) {
            args.search = query;
        }
        const certificates = await new CryptoApi(
            DEFAULT_CONFIG,
        ).cryptoCertificatekeypairsList(args);
        return certificates.results;
    }}
    .renderElement=${(item: CertificateKeyPair): string => {
        return item.name;
    }}
    .value=${(item: CertificateKeyPair | undefined): string | undefined => {
        return item?.pk;
    }}
    .selected=${(item: CertificateKeyPair): boolean => {
        return this.instance?.tlsVerification === item.pk;
    }}
    ?blankable=${true}
>
</ak-search-select>
```

Now looks like:

```HTML
<ak-crypto-certificate-search certificate=${this.instance?.tlsVerification}>
</ak-crypto-certificate-search>
```

There are three searches that do not require there to be a valid key with the certificate; these are
supported with the boolean property `nokey`; likewise, there is one search (in SAMLProviderForm)
that states that if there is no current certificate in the SAMLProvider and only one certificate can
be found in the Authentik database, use that one; this is supported with the boolean property
`singleton`.

These changes replace 382 lines of object-oriented invocations with 36 lines of declarative
configuration, and 98 lines for the class.  Overall, the code for "find a crypto certificate" has
been reduced by 46%.

Suggestions for a better word than `singleton` are welcome!

* web: display tests for CryptoCertificateKeypair search

This adds a Storybook for the CryptoCertificateKeypair search, including
a mock fetch of the data.  In the course of running the tests, we discovered
that including the SearchSelect _class_ won't include the customElement declaration
unless you include the whole file!  Other bugs found: including the CSS from
Storybook is different from that of LitElement native, so much so that the
adapter needed to be included.  FlowSearch had a similar bug.  The problem
only manifests when building via Webpack (which Storybook uses) and not
Rollup, but we should support both in distribution.

* Fixed behavioral problem with the radio; the `if` there was
preventing the radio from reflecting the default correctly.
The observed behavior was that the radio wouldn't "activate"
until the item selected during the render pass was clicked on
first.

* Proxy Provider done.

* web: Tactical change.  Put all the variants on the second page; it's
a longer list, but it's also easier to manage than all those
required sub-options.

* Rounding out the catalog.

* web: SAML Manual Configuration

Added a 'design document' that just kinda describes what I'm trying
to do, in case I don't get this done by Friday Aug 11, 2023.

I had two tables doing the same thing, so I merged them and then
wrote a few map/filters to specialize them for those two use cases.

Along the way I had to fiddle with the ESLint settings so that
underscore-prefixed unused variables would be ignored.

I cleaned up the visual appeal of the forms in the LDAP application.

I was copy/pasting the "handleProviderEvent" function, so I pulled
it out into ApplicationWizardProviderPageBase.  Not so much a matter
of abstraction as just disliking that kind of duplication; it served
no purpose.

* Added SAML Story to Storybook.

* Web: This is coming together amazingly well.  Like, almost too well.

* web: 80% of the way there

This commit includes the first three pages of the wizard, the
completion of the wizard framework with evented handling, and control
over progression.

Some shortcomings of this design have become evident: it isn't
possible to communicate between the steps' wrappers, as they are
POJOs without access to the context.  An imperative decision-making
process has to be inserted in the orchestration layer,
which is kinda annoying.

But it looks good and it behaves correctly, to the extent that I've
given it behavior.  It's an excellent foundation.

* Linting.

* web: application wizard

Found where the hook for form validity should go.  Excellent!  Now I just need to incorporate
that basic validation into the business logic and we're good to go.

* Turns out that was one layer too many; the topmost component was fine for
maintaining the context.

* It looks like my brilliant strategy has hit a snag.

The idea is simple.  Let's start with this picture:

```
<application-wizard .steps=${[... a collection of step objects ...]}>
  <wizard-main .steps=${(steps from above)}>
    <application-current-panel>
      <current-form>
```

- ApplicationWizard has a Context for the ApplicationProviderPair (or whatever it's going to be).
  This context does not know about the steps; it just knows about: the "application" object, the
  "provider" object, and a discriminator to know *which* provider the user has selected.
- ApplicationWizard has Steps that, among other things, provides Panels for:
  - Application
  - Pick Provider
  - Configure Provider
  - Submit ApplicationProviderPair to the back-end
- The WizardFrame renders the CurrentPanel for the CurrentStep

The CurrentPanel gets its data from the ApplicationWizard in the form of a Context. It then sends
messages (events) to ApplicationWizard about the contents of each field as the user is filling out
the form, so that the ApplicationWizard can record those in the ApplicationProviderPair for later
submission.

When a CurrentForm is valid, the ApplicationWizard updates the Steps object to show that the "Next
button" on the Wizard is now available.

In this way, the user can progress through the system.  When they get to the last page, we can
provide in the ApplicationWizard with the means to submit the form and/or send the user back to
the page with the validation failure.

Problem: The context is being updated in real-time, which is triggering re-renders of the form. This
leads to focus problems as the fields that are not yet valid are triggering "focus grab" behavior.
This is a classic problem with "controlled" inputs. What we really want is for the CurrentPanel to
not re-render at all, but to behave like a normal, uncontrolled form, and let the browser do most of
the work.  We still want the [Next] button to enable when the form is valid enough to permit that.

---

Other details: I've ripped out a lot of Jen's work, which is probably a mistake.  It's still
preserved elsewhere.  I've also cleaned up the various wizardly things to try and look organized.
It *looks* like it should work, it just... doesn't.  Not yet.

* Late addition: I had an inspiration about how to reduce the way
reactivity broke focus by, basically, removing the reactivity and
managing the first-time-through lifecycle to prevent the update
from causing refocus.  It works well!  Now I just need to test it.

* This application fixes the bug with respect to the wizard-level context being updated incorrectly.

Understandings:

- To use uncontrolled inputs, which I prefer, the context object should not be a state or property
  at the level of consumers; it should not automatically re-render with every keystroke, i.e. "The
  React Way."  We're using Web Components, [client-side
  validation](https://developer.mozilla.org/en-US/docs/Learn/Forms/Form_validation) exists on the
  platform already, and live-validation is problematic for any number of reasons.
- The trade-off is that it is now necessary to re-render the target page of the wizard de-novo, but
  that's not really as big a deal as it sounds. Lit is ready to do that... and then nothing else
  until we request a change-of-page. Excellent.
- The top level context *must* be a state, but it's better if it's a state never actually used by
  the top-level context container. The debate about whether or not to make that container a dumb one
  (`<slot></slot>`) or to merge it with the top-level object continues; here, I've merged it with
  the top-level wizard object, but that object does not refer to the state variable being managed in
  its render pass, so changes to it do not cause a re-render of the whole wizard. The purpose of the
  top-level page is to manage the *steps*, not the *content of any step*. A step may change
  dynamically based on the content of a step, but that's the same thing as *which step*. Lesson:
  always know what your state is *about*.
- Deep merging is a complex subject, but here it's appropriate to our needs.

* web: Application Wizard

This commit combines a working (but very unpolished) version of the Application Wizard with Jen's
code for the CoreTransactionApplicationRequest, resulting in a successful round trip.

It fixes a number of bugs with the way ContextProducer decorators were being processed, such that
they just weren't working with our current configuration (although they did work fine in Storybook);
consumers didn't need to be fixed.

It also *removes* the steps-aware context from the Wizard.

That *may* be a mistake.  To re-iterate, the `WizardFrame` provides the chrome for a Wizard: the
button bar div, the breadcrumbs div, the header div, and it takes the steps object as its source of
truth for all of the content.  The `WizardContent` part of the application has two parts: The
`WizardMain`, which wraps the frame and supplies the context for all the `WizardPanels`, and the
`WizardPanels` themselves, which are dependent on a context from `WizardMain` for the data that
populates each panel. YAGNI right now that the panels need to know anything about the steps, and the
`WizardMain` can just pass a fresh `.steps` object to the `WizardFrame` when they need updating.
Using props drilling may make more sense here.

It certainy does *not* make sense for the panels.  They need to be renderable on-demand, and they
need to make sense of what they're rendering on-demand, so the function is

```
(panel code) => (context) => (rendered panel)
```

(Yes, that's curried notation. Deal.)

* This commit includes the first WDIO test for the ApplicationWizard.  It doesn't do much right now, but
it does log in and navigate to the wizard successfully.

* web: completed test for single application, provided new programming language to make it easier to write tests.

* Almost there.

Missing: The validation is currently not working as expected, and I cannot get the backend
to give me meaningful data helping us "go back" to the field that wasn't valid.  I really
don't want to put all the meaningful validation on the front-end; that's the road to -
perdition, the back-end must be usable by people less assiduous than we are.

Also: Need to make the button bar work better; maybe each panel can provide a custom button
bar if one is needed?

* web: Test harness

We have an end-to-end test harness that includes a trivially correct DSL for "This is what a user would do, do this":

```
const deleteProvider = (theSlug) => ([
    ["button", '>>>ak-sidebar-item a[href="#/core/providers"]'],
    ["deletebox", `>>>a[href="#/core/applications/${theSlug}"]`],
    ["button", '>>>ak-forms-delete-bulk button[slot="trigger"]'],
    ["button", '>>>ak-forms-delete-bulk div[role="dialog"] ak-spinner-button'],
]);
```

It's now possible to target individual sequences of events this way.  With a little creativity, we could have standalone functions that take parameters for our calls and just do them, without too much struggle.

* web: Revised navigation

After working with the navigation for awhile, I realized that it's a poor map; what I really wanted was
a controller/view pair, where events flow up to the controller and then messages on "what to draw" flow
down to the view.  It work quite well, and the wizard frame is smaller and smarter for it.

I've also moved the WDIO-driven tests into the 'tests' folder, because it (a) makes more sense to put
them there, and (b) it prevents any confusion about who's in charge of node_modules.

* web: Simplify, simplify, simplify

Sort-of.

This commit changes the way the "wizard step coordinator" layer works, giving the
wizard writer much more power over button bar.  It still assumes there are only
three actions the wizard frame wants to commit: next, back, and close.  This empowers
the steps themselves to re-arrange their buttons and describe the rules through which
transitions occur.

* web: resetting the form is not working yet...

I vehemently dislike the object-oriented "reset" command; every wizard should start with
an absolutely fresh copy of the data upon entry.  Refactoring the wizard to re-build its
content from the inside is the correct way to go, but I don't have a good mental image
of how to make the ModalButton and the component it invokes interact cleanly, which
frustrates the hell out of me.

* web: reset

As I said, I greatly dislike having to be dependent upon "resets"; I prefer my
data to be de novo going into a "new" transaction.  That said, we work with
what we've got; I've created an event generated by the wizard that says the
modal just closed; anything wrapping and implementing the wizard can then
capture that event and reset the data.  I've also added a pair of functions
that create the two states (what step, what form data) anew, so that resetting
is as trivial as initializing (and is exactly the same, code-wise).

* web: Without error handling, this is complete, but I still need @BeryJu (Jens)
for help with the SAML Upload (it doesn't appear to be correctly handled?) and
the error handling.

* web: revise tests for wizard

This commit replaces the previous WDIO instance with a more formal and straightforward process using
the [pageobjects](https://martinfowler.com/bliki/PageObject.html).  In this form, every major
component has its own test suite, and a test is a sequence of exercises of those components.

A test then becomes something as straightforward as:

```
        await LoginPage.open();
        await LoginPage.login("ken@goauthentik.io", "eat10bugs");

        expect(await UserLibraryPage.pageHeader).toHaveText("My Applications");
        await UserLibraryPage.goToAdmin();

        expect(await AdminOverviewPage.pageHeader).toHaveText("Welcome, ");
        await AdminOverviewPage.openApplicationsListPage();

        expect(await ApplicationsListPage.pageHeader).toHaveText("Applications");
        ApplicationsListPage.startCreateApplicationWizard();

        await ApplicationWizard.app.name.setValue(`Test application ${newId}`);
        await ApplicationWizard.nextButton.click();
        await (await ApplicationWizard.getProviderType("ldapprovider")).click();
        await ApplicationWizard.nextButton.click();
        await ApplicationWizard.ldap.setBindFlow("default-authentication-flow");
        await ApplicationWizard.nextButton.click();
        await expect(await ApplicationWizard.commitMessage).toHaveText(
            "Your application has been saved"
        );
```

Whether or not there's another layer of DSL in there or not, this is a pretty nice idiom for
maintaining tests.

* web: updating with forms and fixes for eslint complaints.

* web/add webdriverIO testing layer

This commit adds WebdriverIO as an end-to-end solution to unit testing.  WebdriverIO can be run both
locally and remotely, supports strong integration with web components, and is generally robust for
use in pipelines.  I'll confess to working through a tutorial on how to do this for web components,
and this is just chapter 2 (I think there are 5 or so chapters...).

There's a makefile, with help!  If you just run `make` it tells you:

```
Specify a command. The choices are:

  help                 Show this help
  node_modules         Runs `npm install` to prepare this feature
  precommit            Run the precommit: spell check all comments, eslint with sonarJS, prettier-write
  test-good-login      Test that we can log into the server. Requires a running instance of the server.
  test-bad-login       Test that bad usernames and passwords create appropriate error messages
```

... because Makefiles are documentation, and documentation belongs in Makefiles.

I've chosen to go with a PageObject-oriented low-level DSL; what that means is that for each major
components (a page, a form, a wizard), there's a class that provides human-readable names for
human-interactable and human-viewable objects on the page.  The LoginPage object, for example, has
selectors for the username, password, submit button, and the failure alert; accessing those allows
us to test for items as expected., and to write a DSL for "a good login" that's as straightforward
as:

```
        await LoginPage.open();
        await LoginPage.login("ken@goauthentik.io", "eat10bugs");
        await expect(UserLibraryPage.pageHeader).toHaveText("My applications");
```

There was a *lot* of messing around with the LoginPage to get the username and password into the
system.  For example, I had to do this with all the `waitForClickable` and `waitForEnable` because
we both keep the buttons inaccessible until the form has something and we "black out" the page (put
a darkening filter over it) while accessing the flow, meaning there was a race condition such that
the test would attempt to interact with the username or password field before it was accessible.
But this works now, which is very nice.

``` JavaScript
    get inputUsername() {
        return $('>>>input[name="uidField"]');
    }

    get btnSubmit() {
        return $('>>>button[type="submit"]');
    }

    async username(username: string) {
        await this.inputUsername.waitForClickable();
        await this.inputUsername.setValue(username);
        await this.btnSubmit.waitForEnabled();
        await this.btnSubmit.click();
    }
```

The bells & whistles of *Prettier*, *Eslint*, and *Codespell* have also been enabled. I do like my
guardrails.

* web/adding tests: added comments and cleaned up some administrative features.

* web/test: changed the name of one test to reflect it's 'good' status

* core/allow alternative postgres credentials

This commit allows the `dev-reset` command in the Makefile to pick up and use credentials from the
`.env` file if they are present, or fallback to the defaults provided if they are not. This is the
only place in the Makefile where the database credentials are used directly against postgresql
binaries. The syntax was tested with bash, zsh, and csh, and did not fail under those.

The `$${:-}` syntax is a combination of a Makefile idiom for "Pass a single `$` to the environment
where this command will be executed," and the shell expresion `${VARIABLE:-default}` means
"dereference the environment variable; if it is undefined, used the default value provided."

* Re-arrange sequence to avoid recursive make.

Nothing wrong with recursive make; it just wasn't essential
here.  `migrate` is just a build target, not a task.

* Cleanup according to the Usage:
  checkmake [options] <makefile>...
  checkmake -h | --help
  checkmake --version
  checkmake --list-rules Makefile linting tool.

* core: added 'help' to the Makefile

* get postgres config from authentik config loader

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

* don't set -x by default

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

* sort help

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

* update help strings

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

* web: test LDAP wizard sequence

* web: improve testing by adding test admin user via blueprint

* This commit continues the application wizard buildout.  In this commit are the following changes:

- Added SCIM to the list of available providers
- Fixed ForwardProxy so that its mode is set correctly.  (This is a special case in the committer;
  I'm unhappy with that.)
- Fixed the commit messages so that:
  - icons are set correctly (Success, Danger, Working)
  - icons are colored correctly according to state
  - commit message includes a `data-commit-state` field so tests can find it!
- Merged the application wizard tests into a single test pass
- Isolated common parts of the application wizard tests to reduce unnecessary repetition.  All
  application tests are the same until you reach the provider section anyway.
- Fixed the unit tests so they're finding the right error messages and are enabled to display them
  correctly.
- Moved the test Form handlers into their own folder so they're not cluttering up the Pages folder.

* web: add radius to application wizard

This commit continues the application wizard buildout.  In this commit are the following changes:

- Fixed a width-setting bug in the Makefile `make help` feature (i.e "automate that stuff!")
- Added Radius to the list of providers we can offer via the wizard
- Added `launchUrl` and `UI Settings` to features of the application page the wizard can find
- Changed 'SAML Manual Configuration' to just say "SAML Configuration"
- Modified `ak-form-group` to take and honor the `aria-label` property (which in turn makes it
  easier to target specific forms with unit testing)
- Reduced the log level for wdio to 'warn'; 'info' was super-spammy and not helpful.  It can be put
  back with `--logLevel info` from the command line.

* fix blueprints

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

* update package name

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

* add dependabot

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

* prettier run

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

* add basic CI

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

* remove hooks

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

* web: application wizard refactor & completion

This commit refactors the various components of the Wizard and ApplicationWizard, creating a much
more maintainable and satisfying Wizard experience for both developers (i.e, *me* and *Jens* so
far), and for the customer.

The Wizard base has been refactored into three components:

**AkWizardController**

The `AkWizardController` provides the event listenters for the wizard; it hooks them up, recevies the
events, and forwards them to the wizard.  It unwraps the event objects and forwards the relevant
messages contained in the events.  It knows of three event categories:

- Navigation requests (move to a different step)
- Update requests (the current step has updated the business content)
- Close requests (close or cancel the wizard).

**ak-wizard-frame**

The `ak-wizard-frame` is the ModalButton interface.  It provides the Header, Breadcrumbs (nee`
"navigation block"), Buttons, and a DIV into which the main content is rendered.

**AkWizard**

`AkWizard` is an *incomplete* implementation of the wizard. It's meant to be inherited by a child
class, which will implement the rest. It extends `AKElement`. It provides the basic content needed,
such as steps, currentStep (as an index), an accessor for the step itself, an accessor for the
frame, and the interface to the `AkWizardController`.

**ApplicationWizard**

The `ApplicationWizard` itself has been refactored to accommodate these changes. It inherits from
`AkWizard` and provides the business logic for what to do when a form updates, some custom logic for
preventing moving through the wizard when the forms are incomplete, and a persistence layer for
filling out different providers in the same session. It's simplified a *lot*.

The types specified for `AkWizard` are pretty nifty, I think. I could wish the types being passed
via the custom events were more robust, but [strongly typed custom
events](https://github.com/lit/lit-element/issues/808) turn out to be quite the pain in the, er,
neck. As it is, the `precommit` pass did very good at preventing the worst disasters.

The steps themselves were re-written as objects so that they could take advantage of their `valid`
and `disabled` states and provide more meaningful buttons and labels. I think it's a solid
compromise, and it moved a lot of display logic out of the core `handleUpdate()` business method.

The tests, such as they are, are passing.

* Added comment describing new test.

* web: ensuring copy from `main` is canon

* web: fixes after merge

* web: laying the groundwork for future expansion

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

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

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

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

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

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

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

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

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

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

* actually use assignValue or rather serializeFieldRecursive

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

* web: eslint & prettier fixes, plus small aesthetic differences.

* Restoring this file.  Not sure where it disappears to.

* fix label in dark mode

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

* SCIM Manuel -> SCIM

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

* fix lint errors

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

* web: better converter configuration, CSS repair, and forward-domain-proxy

1. Forward Domain Proxy.  I wasn't sure if this method was appropriate for the wizard,
   but Jens says it is.  I've added it.

2. In the process of doing so, I decided that the Provider.converter field was overly
   complexified; I tried too hard to reduce the number of functions I needed to define,
   but in the process outsourced some of the logic of converting the Wizard's dataset
   into a property typed request to the `commit` phase, which was inappropriate.  All
   of the logic about a provider, aside from its display, should be here with the code
   that distinguishes between providers.  This commit makes it so.

3. Small CSS fix: the fields inherited from the Proxy provider forms had some unexpected
   CSS which was causing a bit of a weird indent.  That has been rectified.

* web: running pre-commit after merge.

* web: ensure the applications wizard tests finish after current changes

* prettier has opinions.

* web: application wizard spit & polish

The "ApplicationWizardHint" now correctly uses the localstorage and allows the user to navigate back
and see the message after it's been hidden, so that it will always be available during the test
phase.

The ApplicationList's old "Create Application Form" button has been restored for the purposes of the
test phase.

The ApplicationWizard is now available on both the ApplicationList and ProviderList pages.

Tana and I discussed the microcopy, putting a stronger second-person "You can do..." twist onto the
language, to give the user the sense of empowerment.

The ShowHintController now has both "hide" and "show" operations, to support the hint restoration.

* web: updated storybook stories for the wizard, illustration how "a simple wizard" is configured in source code and tested with storybook.

* web: I hate getting spanked by prettier.

* web: sometimes I wish I had lower standards

Anyway, this was a very stupid bug, because by definition function
definition arguments don't have uses, they're being defined, not
implemented.  Fixed, conf fixed to compensate, and consequences
conquered.

* move context from labs to main

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

* Revert "move context from labs to main"

This reverts commit 3718ee6904.

* web: reify the data loop

I was very unhappy with the "update this dot-path" mechanism I was using earlier; it was hard
for me to read and understand what was happening, and I wrote the darned thing.  I decided instead
to go with a hard substitution model; each phase of the wizard is responsible for updating the
*entire* payload, mostly by creating a new payload and substituting the field value associated
with the event.

On the receiver, we have to do that *again* to handle the swapping of providers when the user
chooses one and then another.  It looks clunky, and it is, but it's *legible*; a junior dev
could understand what it's doing, and that's the goal.

* Revert "web: reify the data loop"

This reverts commit 09fedcacf0.

* web: revert the 'lit' to 'lit-labs' for task and context.

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
2023-10-18 12:43:37 -07:00
21e5441f92 web: patternfly hints as ak-web-component (#7120)
* web: patternfly hints as ak-web-component

Patternfly 5's "Hints" React Component, but ported to web components.
The discovery that CSS Custom Properties are still available in
child components, even if they're within independent ShadowDOMs,
made this fairly easy to port from Handlebars to Lit-HTML.  Moving
the definitions into `:host` and the applications into the root DIV
of the component made duplicating the Patternfly 5 structure
straightforward.

Despite the [Patternfly
Elements]documentation](https://patternflyelements.org/docs/develop/create/),
there's a lot to Patternfly Elements that isn't well documented,
such as their slot controller, which near as I can tell just makes
it easy to determine if a slot with the given name is actually being
used by the client code, but it's hard to tell why, other than that it
provides an easy way to determine if some CSS should be included.

* Pre-commit fixes.

* web: fix some issues with styling found while testing.

* web: separated the "with Title" and "without Title" stories.

* Added footer story, fixed some CSS.

* web: hint controller

Add the `ShowHintController`.  This ReactiveController takes a token
in its constructor, and looks in LocalStorage for that token and
an associated value.  If that value is not `undefined`, it sets the
field `this.host.showHint` to the value found.

It also provides a `render()` method that provides an `ak-hint-footer`
with a checkbox and the "Don't show this message again," and responds
to clicks on the checkbox by setting the `this.hint.showHint` and
LocalStorage values to "false".

An example web component using it has been supplied.

* web: support dark mode for hints.

This was nifty.  Still not entirely sure about the `theme="dark"`
rippling through the product, but in this case it works quite well.
All it took was defining the alternative dark mode values in a CSS
entry, `:host([theme="dark"]) { ... }` and exploiting Patternfly's
already intensely atomized CSS Custom Properties properly.

* web: revise colors to use more of the Authentik dark-mode style.

* Update web/src/components/ak-hint/ak-hint.ts

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

* remove any

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

---------

Signed-off-by: Jens L. <jens@beryju.org>
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L <jens@goauthentik.io>
2023-10-12 10:44:15 -07:00
6792bf8876 web: package up horizontal elements into their own components (#7053)
* web: laying the groundwork for future expansion

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

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

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

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

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

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

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

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

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

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

* actually use assignValue or rather serializeFieldRecursive

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

* web: package up horizontal elements into their own components.

This commit introduces a number of "components."  Jens has this idiom:

```
   <ak-form-element-horizontal label=${msg("Name")} name="name" ?required=${true}>
       <input
           type="text"
           value="${ifDefined(this.instance?.name)}"
           class="pf-c-form-control"
           required
       />
   </ak-form-element-horizontal>
```

It's a very web-oriented idiom in that it's built out of two building blocks, the "element-horizontal" descriptor,
and the input object itself.  This idiom is repeated a lot throughout the code.  As an alternative, let's wrap
everything into an inheritable interface:

```
  <ak-text-input
      name="name"
      label=${msg("Name")}
      value="${ifDefined(this.instance?.name)}
      required
  >
  </ak-text-input>
```

This preserves all the information of the above, makes it much clearer what kind of interaction we're having
(sometimes the `type=` information in an input is lost or easily missed), and while it does require you know
that there are provided components rather than the pair of layout-behavior as in the original it also gives
the developer more precision over the look and feel of the components.

*Right now* these components are placed into the LightDOM, as they are in the existing source code, because
the Form handler has a need to be able to "peer into" the "element-horizontal" component to find the values
of the input objects.  In a future revision I hope to place the burden of type/value processing onto the
input objects themselves such that the form handler will need only look for the `.value` of the associated
input control.

Other fixes:

- update the FlowSearch() such that it actually emits an input event when its value changes.
- Disable the storybook shortcuts; on Chrome, at least, they get confused with simple inputs
- Fix an issue with precommit to not scan any Python with ESLint!  :-)

* web: provide storybook stories for the components

This commit provides storybook stories for the ak-horizontal-element wrappers.  A few
bugs were found along the way, including one rather nasty one from Radio where we
were still getting the "set/unset" pair in the wrong order, so I had to knuckle down
and fix the event handler properly.

* web: test oauth2 provider "guinea pig" for new components

I used the Oauth2 provider page as my experiment in seeing if the
horizontal-element wrappers could be used instead of the raw wrappers
themselves, and I wanted to make sure a test existed that asserts
that filling out THAT form in the ProvidersList and ProvidersForm
didn't break anything.

This commit updates the WDIO tests to do just that; the test is
simple, but it does exercise the `name` field of the Provider,
something not needed in the Wizard because it's set automatically
based on the Application name, and it even asserts that the new
Provider exists in the list of available Providers when it's done.

* web: making sure ESlint and Prettier are happy

* "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>
2023-10-04 13:07:52 -07:00
a0d2aca61c web: detangle components from applications (#6891)
* Web: Detangling some circular dependencies in Admin and User

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

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

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

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

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

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

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

* web: remove admin from elements

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

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

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

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

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

* This was supposed to be merged.

* web: remove `./element`⇢`./user` references

The offender here is UserDevicesList, which despite being in `elements` is only
used by the admin/user/UserViewPage.  The problem is that UserDevicesList,
despite being in `admin`, inherits from `user`, so moving it would have created
a new admin⇢user reference, and the whole point of this exercise is to get rid
of references that point "up" from the foundational pieces to the views, or
that refer to components in sibling applications.

After examining UserDevicesList, I realized that *every feature* of MFADevicesList
had been overridden: the rows, the columns, the toolbar, and the endpoint all had
custom overrides.  Nothing was left of MFADevicesList after that.   Even the
property that the web component used had been completely changed.  The only thing
they had in common was that they both inherited from `Table<Device>`.

Refactoring UserDevicesList so that it inherited directly from `Table<Device>` and
then moving it into `./admin/users` was the obvious and correct step.

Both used the same label table, so that went into the `common/labels` folder.

Along the way, I cleaned up a few minor details. Just little things, like the repeated invocation
of:

```
new AuthenticatorsApi(DEFAULT_CONFIG).authenticatorAdminMETHODDestroy({ id: device.pk });
```

This is repeated five times, once for each Method.  By creating these:

```
        const api = new AuthenticatorsApi(DEFAULT_CONFIG);
        const id = { id: device.pk };
```

The method invocation could be just `api.authenticatorsMETHODDestroy(id)`, which is easier on the
eyes.  See the MFADevicesPage for the full example.

Similarly,

```
return [
   new TableColumn(msg("Name"), ""),
   new TableColumn(msg("Type"), ""),
   new TableColumn("")
];
   ```

is more straightforward as:

```
const headers = [msg("Name"), msg("Type"), ""];
return headers.map((th) => new TableColumn(th, ""));
```

We've labeled what we're working with, and web developers ought to know that `th` is the HTML code
for `table header`.

I've had to alter what files are scanned in pre-commit mode; it doesn't handle renamed files very well,
and at the moment a file that is renamed is not scanned, as its "new" name is not straightforwardly
displayed, not even by `git porcelain`.

* web: make the table of column headers look like a table

* web: detangle `common` from `elements`.

And just like that, `common` no longer has a reference to `elements`.   I don't mind this little bit of
code duplication if it removes a cycle.  What it does point out is that there are bits of `common` that
are predicated on the presence of the browser, and that there are bits of `elements` that, if they rely
on `common`, can't be disentangled from the application as a whole.  Which seems to me that we have two
different things going on in common: things about an application, and things about elements that are
independent of the application.

I'll think about those later.

```
$ rg 'import.*@goauthentik' ./common/ | perl -ne 'm{"(@goauthentik[^"]*)"} && print "$1\n"' | sort | cut -d '/' -f1-2 | uniq | sort
@goauthentik/api
@goauthentik/common
$
```

* web: odd bug; merge-related?  Gonna investigate.

* web: build failure thanks to local cache; fixed

* web: detangle `components` from `admin`.

This was the last inappropriate reference: something from `./components` referencing something in
`./admin`, in this case the `ak-event-info` component.  Used by both Users and Admin, moving it
into `./components` was the obvious correct step.

`ak-event-info` is a lookup table relating specific events in the event log to rich, textual
representations; in the special case of model changes and email info, even more rich content is
available in a dl/dt format. I've tableized the model changes and email info renderer, and I've
extracted every event's textual representation into its own method, converting the `switch/case`
rendering statement into a `switch/case` dispatch switch. This has the virtue of isolating each
unique case and making the dispatch switch short and coherent.

The conversion was done mechanistically; I gave the refactorer (Tide, in this case) instructions to
duplicate the switch block and then convert every case into a method with a name patterned on the
`case`. Going back to the original switch block, it was easy to duplicate the pattern matching and
convert it into a dispatch switch.

And with this, there are zero cycles in the references between the different "packageable" sections
of the UI.  The only thing left to do is figure out how to redistribute `./elements` and `./components`
in a way that makes sense for each.

* Changed function name from 'emailMessageBody' to 'githubIssueMessageBody' to better reflect its usage.

* web: added comments about length and purpose of githubIssueMessageBody.

* Update web/src/common/labels.ts

Co-authored-by: Jens L. <jens@goauthentik.io>
Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>

* Unwanted change.

---------

Signed-off-by: Ken Sternberg <133134217+kensternberg-authentik@users.noreply.github.com>
Co-authored-by: Jens L. <jens@goauthentik.io>
2023-09-14 14:51:42 -07:00
d35c7df789 web: detangle element to admin references (#6864)
* Web: Detangling some circular dependencies in Admin and User

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

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

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

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

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

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

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

* web: remove admin from elements

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

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

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

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

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

* This was supposed to be merged.
2023-09-13 12:28:42 -07:00
28702b3a25 web: Detangling some circular dependencies in Admin and User (#6852)
* Web: Detangling some circular dependencies in Admin and User

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

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

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

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

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

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

Touching the LibraryApplications page triggered a cyclomatic
complexity check.  Extracting the expansion block and streamlining
the class and style declarations with lit directives helped.
2023-09-13 10:16:24 -07:00
bfd0fb66b3 web/admin: fix ak-toggle-group for policy and blueprint uses (#6687)
* web/admin: fix ak-toggle-group for policy and blueprint uses

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

* fix and re-enable lit-analyse

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

---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
2023-08-30 12:46:58 +02:00
f5394da9f7 web: Replace ad-hoc toggle control with ak-toggle-group (#6470)
* web: Replace ad-hoc toggle control with ak-toggle-group

This commit replaces various ad-hoc implementations of the Patternfly Toggle Group HTML with a web
component that encapsulates all of the needed behavior and exposes a single API with a single event
handler, return the value of the option clicked.

The results are: Lots of visual clutter is eliminated.  A single link of:

```
<div class="pf-c-toggle-group__item">
  <button
      class="pf-c-toggle-group__button ${this.mode === ProxyMode.Proxy
          ? "pf-m-selected"
          : ""}"
      type="button"
      @click=${() => {
          this.mode = ProxyMode.Proxy;
      }}>
      <span class="pf-c-toggle-group__text">${msg("Proxy")}</span>
  </button>
</div>
<div class="pf-c-divider pf-m-vertical" role="separator"></div>
```

Now looks like:

```
<option value=${ProxyMode.Proxy}>${msg("Proxy")}</option>
```

This also means that the three pages that used the Patternfly Toggle Group could eliminate all of
their Patternfly PFToggleGroup needs, as well as the `justify-content: center` extension, which also
eliminated the `css` import.

The savings aren't as spectacular as I'd hoped: removed 178 lines, but added 123; total savings 55
lines of code.  I still count this a win: we need never write another toggle component again, and
any bugs, extensions or features we may want to add can be centralized or forked without risking the
whole edifice.

* web: minor code formatting issue.

* web: adding a storybook for the ak-toggle-group component

* Bugs found by CI/CD.

* web: Replace ad-hoc search for CryptoCertificateKeyPairs with crypto-certificate-search (#6475)

* web: Replace ad-hoc search for CryptoCertificateKeyPairs with ak-crypto-certeficate-search

This commit replaces various ad-hoc implementations of `search-select` for CryptoCertificateKeyPairs
with a web component that encapsulates all of the needed behavior and exposes a single API.

The results are: Lots of visual clutter is eliminated.  A single search of:

```HTML
<ak-search-select
    .fetchObjects=${async (query?: string): Promise<CertificateKeyPair[]> => {
        const args: CryptoCertificatekeypairsListRequest = {
            ordering: "name",
            hasKey: true,
            includeDetails: false,
        };
        if (query !== undefined) {
            args.search = query;
        }
        const certificates = await new CryptoApi(
            DEFAULT_CONFIG,
        ).cryptoCertificatekeypairsList(args);
        return certificates.results;
    }}
    .renderElement=${(item: CertificateKeyPair): string => {
        return item.name;
    }}
    .value=${(item: CertificateKeyPair | undefined): string | undefined => {
        return item?.pk;
    }}
    .selected=${(item: CertificateKeyPair): boolean => {
        return this.instance?.tlsVerification === item.pk;
    }}
    ?blankable=${true}
>
</ak-search-select>
```

Now looks like:

```HTML
<ak-crypto-certificate-search certificate=${this.instance?.tlsVerification}>
</ak-crypto-certificate-search>
```

There are three searches that do not require there to be a valid key with the certificate; these are
supported with the boolean property `nokey`; likewise, there is one search (in SAMLProviderForm)
that states that if there is no current certificate in the SAMLProvider and only one certificate can
be found in the Authentik database, use that one; this is supported with the boolean property
`singleton`.

These changes replace 382 lines of object-oriented invocations with 36 lines of declarative
configuration, and 98 lines for the class.  Overall, the code for "find a crypto certificate" has
been reduced by 46%.

Suggestions for a better word than `singleton` are welcome!

* web: display tests for CryptoCertificateKeypair search

This adds a Storybook for the CryptoCertificateKeypair search, including
a mock fetch of the data.  In the course of running the tests, we discovered
that including the SearchSelect _class_ won't include the customElement declaration
unless you include the whole file!  Other bugs found: including the CSS from
Storybook is different from that of LitElement native, so much so that the
adapter needed to be included.  FlowSearch had a similar bug.  The problem
only manifests when building via Webpack (which Storybook uses) and not
Rollup, but we should support both in distribution.
2023-08-28 20:00:25 +02:00