* packages/fonts: add @goauthentik/fonts package
Extract the bundled web fonts out of web/ into a dedicated
@goauthentik/fonts workspace package: the RedHat variable faces, the
PatternFly pficon icon face, and the FontAwesome Free solid face, each
with its upstream license recorded under licenses/.
web/ now pulls the faces and icons from the package's
@goauthentik/fonts/faces.css and @goauthentik/fonts/icons.css entry
points instead of the old #fonts/* import and the inlined pficon.scss
vendor sheet.
Anchor esbuild's [dir] at the monorepo root via outbase so assets pulled
from the out-of-web package no longer resolve to a "_.._" segment, which
Go's //go:embed silently drops from the embedded outpost build.
Co-Authored-By: Ken Sternberg <ken@goauthentik.io>
* packages/theme: add @goauthentik/theme design-system package
Add @goauthentik/theme, which defines authentik's design tokens (color,
typography, fonts, spacing, shape, shadow, motion, z-index) in TypeScript
and builds them into CSS via styleframe.
Token names carry a distinct separator per category so the token type is
recoverable from the name alone. That is worth the verbosity: it keeps
the DX legible and leaves room for automatic theme management and future
tooling built on top of the token set.
Co-Authored-By: Ken Sternberg <ken@goauthentik.io>
* web/theme: add a demo page for the theme tokens
Add a self-contained demo page, built by build-demo.mjs, that renders
every theme token — colors, typography, spacing, shape, shadow, motion —
so the design system can be eyeballed in isolation while it evolves.
Co-Authored-By: Ken Sternberg <ken@goauthentik.io>
* Heal lockfile.
* Fix spelling.
* Fix grouping.
---------
Co-authored-by: Ken Sternberg <ken@goauthentik.io>
* packages/fonts: extract the RedHat variable faces into @goauthentik/fonts
The RedHat typefaces were checked in twice: web/fonts/RedHat held the six
variable faces the app actually loads, and packages/theme carried its own
copy of those plus twenty-four static weights. The variable files were
byte-identical in both places, but the two @font-face sets disagreed on
naming — web bound "RedHatText" to the variable face while the theme
bound it to the static Regular and used "RedHatTextVF" for the variable
one.
Move web's set, which is the canonical one, into a new assets-only
package and drop the theme's copy entirely. The theme now only names the
families; @goauthentik/fonts binds them to files. Nothing about what the
browser loads changes: same bytes, same family names, same vendor layer.
The package has no build step, and only faces.css needs an exports entry
— the url() references beside it resolve relative to that file in both
ESBuild and Vite.
ESBuild's `outbase` is now pinned to the monorepo root. Assets pulled
from a workspace package outside web/ otherwise land under a `..`
segment, which ESBuild sanitizes to `_.._`; Go's `//go:embed dist/*` in
static_outpost.go skips any segment starting with `_`, so the faces would
have gone missing from the embedded outpost build.
* packages/fonts: move the PatternFly icon faces into @goauthentik/fonts
The pficon and Font Awesome faces were declared inside the vendored
PatternFly icon stylesheets, with the font files sitting beside them
under web/src/styles/global/vendor/assets/. That put font bytes in three
places across the repo and tied the faces to stylesheets we re-vendor
whenever PatternFly moves.
Move both families next to the RedHat faces and declare them in a new
icons.css, kept separate from faces.css so a consumer can take the brand
text faces without the icon fonts. The icon classes stay with PatternFly
— only the @font-face rules and the files move. Same bytes: the emitted
assets keep their content hashes.
pficon.scss went too. It was an unreferenced Sass source whose
`$pf-global--fonticon-path` variable had nothing to resolve against.
The copies in shadow/patternfly-base.css are left alone. They come from
the PatternFly package and their font URLs are already stubbed out by the
bundled-text loader, because @font-face has no effect inside a shadow
root — components inherit the faces registered on the document.
* packages/fonts: record the licensing of every bundled font
The package declared MIT and shipped only Red Hat's OFL text, which
described neither what it redistributes nor what redistribution requires.
Three upstreams are bundled, under three different terms:
RedHat text faces OFL-1.1, (c) The Red Hat Project Authors
pficon MIT, (c) Red Hat, Inc., via PatternFly
Font Awesome 5 Free OFL-1.1 for the font files, CC-BY-4.0 for the
icons they encode, (c) Fonticons, Inc.
Declare that as an SPDX expression, add the package's own MIT text, and
ship each upstream's license alongside a NOTICE.md recording provenance
per file. OFL 1.1 requires the copyright notice and license to travel
with any redistributed copy, and CC BY 4.0 requires attribution where the
icons are displayed; `files` now carries all of it into the tarball.
Two things NOTICE.md is explicit about rather than papering over:
@patternfly/patternfly ships no license file, so the MIT text here is Red
Hat's own copied from the sibling @patternfly/icons package; and Font
Awesome's published license carries a copyright line tracking its current
release rather than the 5.x release these files came from.
* server/static: cover range requests for static assets
The Go web server this originally fixed is gone — `ak server` now execs the
Rust binary, and its static handler is a tower-http `ServeDir` behind a
compression layer. Both halves of the old fix are moot there: `ServeDir`
serves ranges itself, tower-http never compresses a response carrying
`Content-Range`, and no ETag middleware survived the rewrite.
Nothing to port, then, but the events map still byte-serves its PMTiles
basemap out of `/static/dist/`, so pin the behavior it depends on: a ranged
request comes back as an uncompressed 206 with an accurate `Content-Length`,
while a full request is still gzipped. The compression layer moves behind a
named constructor so the test exercises the same configuration the router
builds.
refs #21849
* brands: add branding_map_tiles for the events map tile source (#24253)
* brands: add branding_map_tiles for the events map tile source
Brand-level override for where the events map loads its vector tiles:
empty keeps the bundled basemap, a pmtiles:// archive URL or XYZ template
points at your own. Includes the migration, schema, and regenerated
clients.
* Potential fix for pull request finding
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
* packages/geo: add @goauthentik/geo and the hexworld event map (#24255)
A standalone Lit + MapLibre package for the events map: a tilted globe
that bins events into H3 cells and raises them as action-colored pie
columns, over a hex basemap bundled as a PMTiles archive — no tile server
and no external requests, so it works air-gapped. Zoom bands cross-fade
and columns animate between re-bins. Ships the archive, the generator CLI,
and node tests for the geometry, styling, and tiling plan.
Co-authored-by: Teffen Ellis <teffen@Teffens-MacBook-Pro.local>
---------
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@Teffens-MacBook-Pro.local>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
* web: replace the OpenLayers events map with ak-map (#24257)
* brands: add branding_map_tiles for the events map tile source
Brand-level override for where the events map loads its vector tiles:
empty keeps the bundled basemap, a pmtiles:// archive URL or XYZ template
points at your own. Includes the migration, schema, and regenerated
clients.
* packages/geo: add @goauthentik/geo and the hexworld event map
A standalone Lit + MapLibre package for the events map: a tilted globe
that bins events into H3 cells and raises them as action-colored pie
columns, over a hex basemap bundled as a PMTiles archive — no tile server
and no external requests, so it works air-gapped. Zoom bands cross-fade
and columns animate between re-bins. Ships the archive, the generator CLI,
and node tests for the geometry, styling, and tiling plan.
* web: replace the OpenLayers events map with ak-map
EventMap now renders @goauthentik/geo's globe: events show as extruded
columns split by action, hovering shows a localized breakdown, and
clicking a column filters the list to that cell's events. The build copies
the bundled archive and glyphs into dist. OpenLayers and the pin-marker
path are removed.
---------
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Co-authored-by: Teffen Ellis <teffen@Teffens-MacBook-Pro.local>
* Format.
* brands: note that branding_map_tiles is served unauthenticated
The events map is admin-only, but branding_map_tiles rides along in
CurrentBrandSerializer, which /core/brands/current/ exposes with
AllowAny. Commercial tile providers carry their API key in the URL, and
the help text invites pasting exactly such a URL, so say plainly that
the value is world-readable.
Migration 0016 is edited in place rather than superseded — it has not
shipped, and makemigrations reports no pending changes. Schema and
clients regenerated.
* geo: strip build-machine paths from the shipped basemap archive
The committed hexworld.pmtiles carried the absolute path of the scratch
directory it was built in — including a session uuid — in its metadata
name, description and generator_options, shipped to every install.
tile-join inherits those fields from its first input file, so pass
--name/--description/--attribution explicitly to stop it recurring.
The archive itself is rewritten in place rather than regenerated:
pmtiles v3 lays out header, root directory, metadata, leaf directories
and tile data contiguously, and directory entries address tiles relative
to tileDataOffset, so resizing the metadata only shifts two header
offsets. Verified with the pmtiles reader — header fields match and 634
sampled tiles across z0-7 are byte-identical.
* web: drop the unused OpenLayers map pin
map_pin.svg was the marker icon the OpenLayers events map drew; ak-map
renders extruded columns instead and nothing references the file.
Also correct the preserveSymlinks comment. The flag is load-bearing, but
not for the stated reason: geo resolves its own dependencies fine from
its own node_modules. What it prevents is resolving them by realpath,
which pulls a second copy of the Lit runtime out of .pnpm alongside the
one in web/node_modules — two lit-html/lit-element/@lit-reactive-element
trees and two ReactiveElement hierarchies in one bundle.
* build: pin playwright through the pnpm catalog in both workspaces
The root and web/ are separate pnpm workspaces with separate lockfiles,
so a caret range let them resolve playwright independently — root landed
on 1.62.0 while web sat at 1.61.1, and `playwright install` downloads a
~95 MB browser build keyed to the exact version. Two versions, two
downloads. Catalog entries plus lint-catalogs turn that drift into a
failing check; the pin is exact because a range is what allowed it.
Held at 1.61.1 rather than the newest: 1.62.0 cannot resolve a bare
package name in a tsconfig `extends`, and web/tsconfig.json extends
"@goauthentik/tsconfig", so loading web's playwright.config.js fails and
the e2e suite never runs. `playwright test --list` discovers 25 tests on
1.61.1 and dies before discovery on 1.62.0.
vitest, vite and the @vitest/browser pair join the same catalog since
geo now uses them too and @vitest/browser-playwright drives whichever
playwright it finds.
* geo: rebuild ak-map on re-parent, run tests from source under vitest
disconnectedCallback tore the MapLibre instance down but firstUpdated
only ever fires once, so re-parenting <ak-map> left it permanently
blank. Rebuild from connectedCallback once the element has updated.
The new Chromium test covers exactly that: it fails without the fix and
passes with it, and no other test moves.
Tests move from node:test over compiled out/*.js to vitest over src, so
they exercise the source rather than a stale build and need no build
step. Six of them reached for ../out/*.js through a dynamic import and
would have kept asserting against whatever was last compiled.
Along the way, three things that were already broken:
- `tsc -p scripts` never ran anywhere and does not compile — its
tsconfig omits the DOM lib while its import graph reaches
src/style.ts, which uses `window`. There is now a lint:types script
covering src, scripts and test.
- TippecanoeFeature extended a bare Feature though placeFeature
always emits a Point with fixed properties.
- The README described a previous generation of the generator: wrong
script path, wrong cut names, a zoom band that stops at z7 rather
than z8, a shipped archive listed at 8.8 MB when it is 22 MiB, and
markers painted "via MapLibre feature-state" when they are a
fill-extrusion source.
publishConfig is dropped rather than `private`: geo depends on
@goauthentik/api via link:, which cannot survive publication, so the
package is unpublishable either way and publishConfig was the dead half.
* rust nits
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* website/docs: document the hexworld event map
* website/docs: drop the OSM tile server from the air-gapped outbound list
The events map no longer reaches tile.openstreetmap.org — the bundled
basemap makes no outbound connections. Note the one way it can reach out
again: a custom basemap configured on a brand.
* Ignore build info.
* Ignore build info.
* Fix pins.
* Fix formatting.
* Fix duplicate package entries.
* Move runtime code to web.
---------
Signed-off-by: Teffen Ellis <592134+GirlBossRush@users.noreply.github.com>
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Teffen Ellis <teffen@Teffens-MacBook-Pro.local>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* web: sanitize lit-localize over-escape across all str-tagged messages
`@lit/localize-tools@0.8.x` HTML-escapes every text fragment it splices
into a compiled message template, including `str`-tagged and untagged
messages whose runtime value is a plain string. That bakes literal `<`,
`>`, and `&` characters into the output as `<`, `>`, `&`, and
re-escapes a translator's `"` to `&quot;` — both surfacing as
visible entity text wherever a translated message lands in a DOM text
node. #22857 fixed one case by relocating the angle brackets outside the
`msg(str…)` wrapper. The defect class itself was wider than that one
string.
The XLIFFs mirror Transifex, so an in-repo `<target>` cleanup gets
overwritten on the next sync. The fix lives in the build pipeline:
`build-locales.mjs` now runs `scripts/unescape-locale-entities.mjs`
after `RuntimeLitLocalizer.build()`, which walks every emitted
`src/locales/*.ts` and decodes the XML predefined entities (`<`,
`>`, `"`, `'`, `&`) back to characters for `str`-tagged
and untagged template literals. `html`-tagged literals get a milder
pass that strips one layer of `&`-doubling, so a legitimate `>`
stays single-encoded and lit-html still parses it back to `>`.
`PasswordPolicyForm.ts` also had five `msg("… (guesses < 10^3)")`
source-side strings using literal entities for the score-threshold
helper text. Rewrote them with real `<` / `>=` characters and gave them
explicit `policy.password.score-threshold.description.*-guessable` IDs
per the kebab-hierarchy convention in `web/AGENTS.md`. The
re-extraction sweep dropped the five orphaned auto-hashed trans-units
out of every XLIFF; the next Transifex sync will refill the new IDs.
A unit test covers the sanitizer for str-tagged, untagged, html-tagged,
substitution holes, fully doubled entities, and the no-op
pass-through. A headless Chromium run signed in as akadmin in de-DE
and fi-FI finds zero `&(amp|lt|gt|quot|apos);` hits across body text,
aria-labels, titles, and placeholders.
Co-Authored-By: ${GIT_CO_AUTHOR_NAME} <${GIT_CO_AUTHOR_EMAIL}>
* web: ignore foreign-language locale test fixtures in cspell
* core: add .npmrc baseline to block dependency lifecycle scripts
Set ignore-scripts=true at the repo root, plus engine-strict, save-exact,
audit, and prefer-offline. This neutralizes the dominant npm supply-chain
attack vector — postinstall scripts in transitive dependencies — at the
cost of requiring an explicit rebuild for the handful of packages that
legitimately need install scripts (esbuild, chromedriver, tree-sitter,
tree-sitter-json). The next commit wires that rebuild into the Makefile.
Co-Authored-By: Playpen Agent <279763771+playpen-agent@users.noreply.github.com>
* core: route node installs through make to retire website preinstall hook
Make docs-install depend on a new root-node-install so the root deps
are guaranteed before the website install runs, removing the need for
the website/preinstall lifecycle script. Rebuild the small audited list
of trusted packages (esbuild, chromedriver, tree-sitter, tree-sitter-json)
after the web install so ignore-scripts=true remains the only path that
needs maintenance. web/README documents the new workflow.
Co-Authored-By: Playpen Agent <279763771+playpen-agent@users.noreply.github.com>
* Clean up install scripts.
* Track .npmrc in CODEOWNERS
* Fix formatter config. Reformat.
* Fix mounted references.
* Flesh out node scripts.
* Bump engines.
* Prep containers.
* Update makefile.
* Flesh out github actions.
* Clean up docs container.
* lint.
Bump.
Lint.
Bump NPM version.
* Add limits.
* collapse the composite's three setup-node calls to one cache restore
* Add SHA.
* Bump NPM range.
* Run formatter.
* Bump NPM.
* Remove extra install.
* Fix website deps.
* Use local prettier. Fix drift in CI.
* ci: build frontend in CI with node_env production
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* Install docusaurus config.
* Fix linter warning, order.
* Add linter commands.
* Add timeout.
* Remove pre install check.
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Playpen Agent <279763771+playpen-agent@users.noreply.github.com>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
* web/core/templates: make it possible for interfaces to designate alternative stylesheets
## What
Moves the stylesheet invocation in `theme.html` to `skeleton.html`, give it a block and a block name so that pages using `skeleton.html` can override or extend it as needed.
## Why
The biggest wall we’re hitting right now is the lack of flexibility at the very top of the CSS. We simply use the same CSS file for *too much*, when really we should be thinking in terms of leaner, more targeted top-level CSS for some things, and more rich and expressive CSS when it’s necessary.
The style sheet was being loaded unconditionally in `theme.html`; it’s not in a conditional statement or overridable where it was; `skeleton` just loads it blindly. This change lets `theme.html` be what it is meant to be, an isolated container for the JavaScript logic for discerning the color mode, while enabling CSS developers to elide the stylesheet, provide alternative stylesheets, or (using `{{ block.super}}`) amend or extend the default stylesheet.
* Isolated flows to have their own CSS barrel file.
* Missed a spot.
* web: Fix missing block modifier. Remove redundant.
* Fix spacing.
* Fix status colors on dark themes.
* Add missing dark theme styles. Fix order of style rules.
* Only show ungrouped label when in high contrast.
* Consistent delete disable color.
* Split Patternfly variables into separate files. Prep for PF5.
* Fix contrast on light mode.
* Fix background base color.
* Fix issues surrounding color layering, overlay states, contrast, etc.
* Flesh out placeholder.
* Flesh out loading state, inspector toggle.
* Build static CSS for proxy. Remove loading entry.
* Fix types.
* some very small style adjustments
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
---------
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
* web: Clarify required marker when using screen reader.
* web: Mark helper text as input descriptor.
* web: Use next domain when in development.
* web: Clean up constants. Fix attribute mapping.
* web: use previous function name.
* web: Fix sort.
* web: Use constant.
* web: Use prefix.
* web: keep using current release for notes.
web: Fix issues surrounding markdown rendering.
- Fix issue where Mermaid diagrams do not render.
- Fix link colors in dark mode.
- Fix anchored links triggering router.
- Fix issue where links occasionally link to missing page.
* 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>
* 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: simplify `?inline` handler for Storybook
# What
- Revise the `?inline` handler for Storybook
- Enable headless test runs of E2E
- Reduce headless testing to single instances
# Why
## `?inline` handling
Vite-for-Storybook-for-Web-Components has a requirement that all component CSS imports be
suffixed with an `?inline` argument so Vite knows to put the CSS into the component and
not inject it into the document head.
This `?inline` argument is an implementation detail of Storybook. It would be irrelevant clutter
added to our codebase. We were using `rollup-plugin-modify` to find every instance of an
import-to-component, but the implementation was clunky and involved scanning the source code
manually.
`rollup-plugin-modify` version 3 has regular expressions and takes a function as an argument. This
allows us to generate the CSS import maps on-the-fly when Storybook is run, eliminating a fragile
build step. We can also remove the source code scanner for those imports.
## Changes to testing
It's just nice to be able to run the E2E tests headlessly, without them eating up your screen real
estate, flashing, or grabbing your mouse.
WebdriverIO's testing of Web Components is new and, as we've seen, a bit cranky. The WebdriverIO
team currently recommends not running the tests in parallel. We only have about 70 tests so far, and
they're fairly speedy, especially when you don't have to invoke a browser session for every test.
* Just cleaning up.
* web: removing sonarjs from yet another branch.
* web: everything except the tests are up-to-date. There was a lot, it turns out, we simply weren't using.
* web: update package.json to support WebdriverIO 9
This commit:
- Upgrades to WebdriverIO 9.1.2
- Resets our `devDependencies` collection to remove all imports that we either were not using or
were duplicates of existing dependencies:
- *Babel*, of all things
- Storybook addon css user preferences, now native to Storybook 8
- SonarJS, *again*, sigh.
- React
- Fixes a bug where ESLint would report missing features in our build scripts
- Fixes a bug where Wdio might not reach a headless browser before timeout
- Replaces Rollup's CSSLit with Vite's CSSLit, which actually works without hacks, for testing.
- Moves the package-lock scanner to its own script, with better reporting and tool verification,
which also cleans up the package.lock file a little.
* Prettier just havin' all the opinions.
- Adjust the WebdriverIO configuration so that `maxInstances` can be set by the environment
- `MAX_INSTANCES=1 CI=true npm run test` will run the headless tests from your command line.
- `MAX_INSTANCES=1 npm run test-watch` if you want to watch the test run in-browser.
- Adds `knip` import tracing facility for linting.
- Knip can be accessed by `npm run lint:imports`.
Running `MAX_INSTANCES=10` (the default) would sometimes create conflicts and overwhelm the test
runner, leaving you with 11 open instances of Chrome and no way to know which one is the one you
*don't* want to close. A better choice is `MAX_INSTANCES=1 npm run test-watch`, which would allow
the developer to watch the test run serially.
Knip adds a new linting feature: tracking down which imports are not used, are not exported
correctly, or shouldn't have been exported at all. Despite the "zero-config" promise, it still
required significant configuration to handle the wide variety of "strengths" of ESlint
configurations, as well as pointers to our entries and reminders that web components may export
their classes but their actual use is as part of the component registry.
Knip's analyzer produces a lot of false positives. It is not intended to be used as part of the
CI/CD pipeline; it is there to help developers figure out what can and should be cleaned up
manually.
Co-authored-by: Jens Langhammer <jens@goauthentik.io>
* 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: much better focus discipline
Fix the way focus is handled in SearchSelect so that the drop-down isn't grabbing the focus away
from the Input when the user wants to type in their selection.
Because it was broken otherwise!
There's still a bug where it's possible to type in a complete value
*Label*, then leave the component's focus (input and menu) completely,
in which case the Label remains, looking innocent and correct, but
it is *not* reflective of the value as understood by the SearchSelect
API controller.
Gonna try to fix that next. But I'm saving this as a useful checkpoint.
* .
* root: insert daphne app in correct order
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: implement ak-list-select
Creates a new element, ak-list-select, which is a scrollable list that reports when an element is clicked or
selected by the keyboard.
I was hideously over-engineering ak-search-select-menu, and I decided to try something simpler. This is
that something. The events we care about are just "change" and "lost focus", and both of those can be
attached by the parent regardless of portaling.
* web: ak-list-select is complete
An extraction of the "menu" and "list" features from SearchSelect
and DualSelect, this is a very simplified version of a visible list
that emulates the Radio/Select behavior (i.e only one from the
collection may be "valued" at the time). It has no visible indicators
of selection (aside from some highlighting), as it's meant to be
used to present the list rather than be indicative of any state of
the list.
I was seriously over-engineering the menu. It turns out, it's just
not that difficult after all. The only things we care about, really,
are "did the user change the selection," "did the user click out
of the list," and "did the user press the escape key." Those are
pre-existing events (click w/value, blur, and keydown w/keycode,
respectively), so there was no need for me to introduce new custom
events to handler them.
* web: downgrade sonarjs again, because dependabot
Dammit, really need to tell that machine to leave our versions alone.
* web: search select
After a lot of testing and experimenting, it's finally starting to look stable.
What a pain in the neck this has all been.
* web: hold
* web: search select with focus and progressive search
- New component: ak-list-select, which allows you to select from a list of elements, with keyboard
control.
- New component: ak-portal, which manages elements by moving "slotted" content into a distant
component, usually one attached to the body, and positions it relative to an existing element.
- ak-search-select-view has been revamped to handle focus, change, input, and blur using
the browser native event handlers, rather than inventing my own.
- ak-search-select has been turned into a simple driver that manages the view.
- ak-search-select has a new declarative syntax for the most common use case.
I seriously over-engineered this thing, leaning too heavily on outdated knowledge or assumptions
about how the browser works. The native event handlers attached at the component's borders works
more than fine, and by attaching the event handlers to the portaled component before sending it
off to the slots, the correct handlers get the message. This revision leverages the browser
a *lot* more, and gets much more effective interaction with much less code.
`<ak-list-select>` is a new component that replaces the ad-hoc menu object of the old SearchSelect.
It is a standalone component that just shows a list, allows someone to navigate that list with the
keyboard or the mouse. By default, it is limited to half the height of the viewport.
The list does not have an indicator of "selected" at this time. That's just a side effect of it
being developed as an adjunct to search-select. Its design does not preclude extension.
It has a *lot* of CSS components that can be customized. The properties and events are documented,
but there is only one event: `change`. Consistent with HTML, the value is not sent with the `change`
event; clients are expected to extract it with `change:event.target.value`.
Like all HTML components, it is completely stringly defined; the value is either a string or
undefined.
`<ak-portal>` is a somewhat specialized "portal" component that places an `ak-list-select` in an
object on top of the existing DOM content. It can generalized to do this with any component, though,
and can be extended. It has no events or CSS, since it's "just" managing the portaling relationship.
`<ak-search-select-view>` is the heart of the system. It takes a collection options and behaves
like an autocomplete component for them. The only unique event it sends out is `change`, and like
`ak-list-select`, it expects the client to retrieve the value.
Like all HTML components, it is completely stringly defined; the value is either a string or
undefined.
This is the SearchSelect component we've all known to come and love, but with a better pop-up and
cleaner keyboard interaction. It emits only one event, `ak-change`, which *does* carry the value
with it.
The Storybooks have been updated to show the current version of Search Select, with a (simulated)
API layer as well as more blunt stringly-typed tests for the View layer. A handful of tests have
been provided to cover a number of edge cases that I discovered during testing. These run fine
with the `npx` command, and I would love to see them integrated into CI/CD.
The search select fields `renderElement`, `renderDescription`, and `value` properties of
`ak-search-select` have been modified to take a string. For example, the search for the
list of user looks like this:
```
<ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => {
const args: CoreUsersListRequest = { ordering: "username" };
if (query !== undefined) {
args.search = query;
}
const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args);
return users.results;
}}
.renderElement=${(user: User): string => {
return user.username;
}}
.renderDescription=${(user: User): TemplateResult => {
return html`${user.name}`;
}}
.value=${(user: User | undefined): string | undefined => {
return user?.username;
}}
></ak-search-select>
```
The most common syntax for the these three fields is "just return the string contents of a field by
name," in the case of the description wrapped in a TemplateResult with no DOM components. By
automating that initialization in the `connectedCallback` of the `ak-search-select` component,
this object would look like:
<ak-search-select
.fetchObjects=${async (query?: string): Promise<User[]> => {
const args: CoreUsersListRequest = { ordering: "username" };
if (query !== undefined) {
args.search = query;
}
const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList(args);
return users.results;
}}
.renderElement=${"username"}
.renderDescription=${"name"}
.value=${"username"}
></ak-search-select>
```
Due to a limitation in the way properties (such as functions) are interpreted, the syntax
`renderElement="username"` is invalid; it has to be a property expression. Sorry; best I could do.
The old syntax works just fine. This is a "detect and extend at runtime" enhancement.
* Added comments to the Component Driver Harness.
* Added more safety and comments.
* web: remove string-based access to API; replace with a consolidated "adapter" layer.
Clean out the string-based API layer in SearchSelect. Break SearchSelect into a
"Base" that does all the work, and then wrap it in two different front-ends:
one that conforms to the old WCAPI, and one with a slightly new WCAPI:
```
<ak-search-select-ez
.config=${{
fetchObjects: async (query?: string): Promise<Group[]> => {
const args: CoreGroupsListRequest = {
ordering: "name",
includeUsers: false,
};
if (query !== undefined) {
args.search = query;
}
const groups = await new CoreApi(DEFAULT_CONFIG).coreGroupsList(
args,
);
return groups.results;
},
renderElement: (group: Group): string => group.name,
value: (group: Group | undefined): string | undefined => group?.pk,
selected: (group: Group): boolean => group.pk === this.instance?.group
}}
blankable
>
</ak-search-select-ez>
```
* Prettier had opinions. In one case, an important opinion.
* Rename test and fix lint error.
* 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>
* 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.
* root: fix migrations missing using db_alias
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* more
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
* web: add wireit as a dependency and move SFE into an independent package
* web: make `sfe` a legitimite subpackage and use `wireit` to control the build
- Move sfe to a `packages` subfolder: this is a more standard format for subpackages
- `Move sfe/index.ts` to `sfe/src/index.ts`: this is a more standard layout for a package
- Adjusted paths is `package.json` and `sfe/rollup.config.js` accordingly.
- Add prettier and safety linting to `sfe`.
- fix a naming issues in `build-locales`, highlighted by eslint
- fix some minor linting issues is `build-locales`
- add comments to `build-locales`, to make it clear what it does
- updated the README and LICENSE files
- start using `wireit` heavily as the task-runner definition language
Primarily, to look professional and pave the way for future enhancements.
Aside from the standardization and so forth, the primary goal here is to move our task runner to
wireit. Wireit offers a number of intriguing abilities with respect to caching, building, and
testing, such as an ability to `watch` our folders and files and automatically re-run the build when
the relevant code changes, without having to rebuild the copied content or sub-packages such as
`sfe`.
The ability to pass in environment variables without needed `cross-env` makes code that required it
much easier to read.
Commands that take a long time can be prefixed with the environment variable `${NODE_RUNNER} `,
which then would allow you to default to using `node`, but by setting `NODE_RUNNER` in your shell
you could specify `bun` (or `deno`, maybe, but I haven't tested it with `deno`). `bun` runs the
`eslint` pass in about three-quarters the time `node` takes.
This commit exists primarily to ensure that the build runs as expected under CI, and the result is
as expected under CI.
Wireit was produced by Google and is used by Adobe Spectrum Components, Patternfly Components,
Material Web, Red Hat Design, and the Lit-Element teams, so I'm confident that it's robust and
reliable as a build runner.
* Merge failed to account for this.
* web: fix bad reference to lint command
* Adding sfe to workspaces means its install is run automatically.
* sfe build is now orchestrated by the web build process
* web: slowly tracking down the old ways.
* Trying to fix lit-analyze pass.
* Still struggling with the build.
* Monorepo, please.
* Still trying to solve swc binding issue.
* Reformat package.json so that scripts and wireit are closer to one another.
* Use the right formatter for packagefiles.
* Retarget dockerfile to have the right paths to sfe during build.
* Comment to explain gitignore update.
* Add lint correcting to package.json as well as package-lock
* Restored lost package-lock.json
* Updating the authentik version.
* Trying to force version consistency.
---------
Signed-off-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>
Co-authored-by: Marc 'risson' Schmitt <marc.schmitt@risson.space>