web: Add storybook (#5865)
* \#\# Details
web: replace lingui with lit/localize
\#\# Changes
This rather massive shift replaces the lingui and `t()` syntax with lit-localize, XLIFF, and the `msg()`
syntax used by lit-localize. 90% of this work was mechanized; simple perl scripts found and replaced
all uses of `t()` with the appropriate corresponding syntax for `msg()` and `msg(str())`.
The XLIFF files were auto-generated from the PO files. They have not been audited, and they should be
checked over by professional translators. The actual _strings_ have not been changed, but as this was
a mechanized change there is always the possibility of mis-translation-- not by the translator, but by
the script.
* web: revise lit/localize: fix two installation issues.
* web: revise localization
TL;DR:
- Replaced all of Lingui's `t()` syntax with `msg()` syntax.
- Mechanically (i.e with a script) converted all of the PO files to XLIFF files
- Refactored the localization code to be a bit smarter:
- the function `getBestMatchLocale` takes the locale lists and a requested locale, and returns the
first match of:
- The locale's code exactly matches the requested locale
- The locale code exactly matches the prefix of the requested locale (i.e the "en" part of "en-US")
- the locale code's prefix exactly matches the prefix of the requested locale
This function is passed to lit-locate's `loadLocale()`.
- `activateLocale()` just calls `loadLocale()` now.
- `autodetectLanguage` searches the following, and picks the first that returns a valid locale
object, before passing it to `loadLocale()`:
- The User's settings
- A `?locale=` component found in `window.location.search`
- The `window.navigator.language` field
- English
The `msg()` only runs when it's run. This seems obvious, but it means that you cannot cache
strings at load time; they must be kept inside functions that are re-run so that the `msg()` engine
can look up the strings in the preferred language of the user at that moment.
You can use thunks-of-strings if you really need them that way.
* Including the 'xliff-converter' in case anyone wants to review it.
* The xliff-converter is tagged as 'xliff-converter', but has been
deleted.
\#\# Details
- Resolves #5171
\#\# Changes
\#\#\# New Features
- Adds a "Add an Application" to the LibraryView if there are no applications and the user is an administrator.
\#\#\# Breaking Changes
- Adds breaking change which causes \<issue\>.
\#\# Checklist
- [ ] Local tests pass (`ak test authentik/`)
- [ ] The code has been formatted (`make lint-fix`)
If an API change has been made
- [ ] The API schema has been updated (`make gen-build`)
If changes to the frontend have been made
- [ ] The code has been formatted (`make web`)
- [ ] The translation files have been updated (`make i18n-extract`)
If applicable
- [ ] The documentation has been updated
- [ ] The documentation has been formatted (`make website`)
* web: fix redundant locales for zh suite.
* web: prettier pass for locale update
* web: localization moderization
Changed the names of the lit-localize commands to make it clear they're
part of the localization effort, and not just "build" and "extract".
* web: add storybook to test components
* update transifex config
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix package lock?
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* use build not compile
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: conversion to lit-localize
The CI produced a list of problems that I hadn't caught earlier,
due to a typo ("localize build" is correct, "localize compile" is
not) I had left in package.json. They were minor and linty, but
it was still wise to fix them.
* web: replace lingui with lit/locale
This commit fixes some minor linting issues that were hidden by a typo in package.json. The
issues were not apparently problematic from a Javascript point of view, but they pointed
to sloppy thinking in the progression of types through the system, so I cleaned them
up and formalized the types from LocaleModule to AkLocale.
* web: replace lingui with lit/localize
One problem that has repeatedly come up is that localize's templates do not produce
JavaScript that conforms with our shop style. I've replaced `build-locale` with
a two-step that builds the locale *and* ensures that it conforms to the shop style
via `prettier` every time.
* web: replace lingui with lit-locale
This commit applies the most recent bundle of translations to the
new lit-locale aspect component. It also revises the algorithm
for *finding* the correct locale, replacing the complex fall-back
with some rather straightforward regular expressions.
In the case of Chinese, the fallback comes at the end of the
selection list, which may not be, er, politically valuable
(since Taiwan and Hong Kong come before, being exceptions that
need to be tested). If we need a different order for presentation,
that'll be a future feature.
* web: replace lingui with lit/locale
Well, that was embarassing.
* web: add storybook
The delta on this didn't make any sense; putting it back causes no behavioral
changes.
* web: add Storybook
Fixed a typo in the package.json that prevented the TSC check
from passing.
* web: incorporate storybook
This commit includes a number of type and definitional changes needed to make lit-analyze pass. In
most cases, it was a matter of reassuring Lit that we were using the right type and the right type
converter, or configuring the property such that it should never be called as an attribute.
The most controversial change is adding the 'no-incompatible-type-binding' to the LIT analyzer
configuration (found in `tsconfig.json`). This "routes around" lit-analyzer not doing very well
understanding that some HTML objects can have generic property types, as long as the renderer is
configured correctly.
The 'no-missing-import: off' setting is required as lit-analyzer also does not use the tsconfig
`paths` setting correctly and cannot find objects defined via aliases.
It's a shame JSON can't support comments; these should be in the tsconfig.json file directly. As it
is, I've started a README file that includes a section to record configuration decisions.
Deleted the lingui.config file as we're not using it anymore
* ignore storybook build in git
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-06-07 11:05:33 +00:00
|
|
|
# authentik WebUI
|
|
|
|
|
|
|
|
This is the default UI for the authentik server. The documentation is going to be a little sparse
|
|
|
|
for awhile, but at least let's get started.
|
|
|
|
|
web: abstract `rootInterface()?.config?.capabilities.includes()` into `.can()` (#7737)
* This commit abstracts access to the object `rootInterface()?.config?` into a single accessor,
`authentikConfig`, that can be mixed into any AKElement object that requires access to it.
Since access to `rootInterface()?.config?` is _universally_ used for a single (and repetitive)
boolean check, a separate accessor has been provided that converts all calls of the form:
``` javascript
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)
```
into:
``` javascript
this.can(CapabilitiesEnum.CanImpersonate)
```
It does this via a Mixin, `WithCapabilitiesConfig`, which understands that these calls only make
sense in the context of a running, fully configured authentik instance, and that their purpose is to
inform authentik components of a user’s capabilities. The latter is why I don’t feel uncomfortable
turning a function call into a method; we should make it explicit that this is a relationship
between components.
The mixin has a single single field, `[WCC.capabilitiesConfig]`, where its association with the
upper-level configuration is made. If that syntax looks peculiar to you, good! I’ve used an explict
unique symbol as the field name; it is inaccessable an innumerable in the object list. The debugger
shows it only as:
Symbol(): {
cacheTimeout: 300
cacheTimeoutFlows: 300
cacheTimeoutPolicies: 300
cacheTimeoutReputation: 300
capabilities: (5) ['can_save_media', 'can_geo_ip', 'can_impersonate', 'can_debug', 'is_enterprise']
}
Since you can’t reference it by identity, you can’t write to it. Until every browser supports actual
private fields, this is the best we can do; it does guarantee that field name collisions are
impossible, which is a win.
The mixin takes a second optional boolean; setting this to true will cause any web component using
the mixin to automatically schedule a re-render if the capabilities list changes.
The mixin is also generic; despite the "...into a Lit-Context" in the title, the internals of the
Mixin can be replaced with anything so long as the signature of `.can()` is preserved.
Because this work builds off the work I did to give the Sidebar access to the configuration without
ad-hoc retrieval or prop-drilling, it wasn’t necessary to create a new context for it. That will be
necessary for the following:
TODO:
``` javascript
rootInterface()?.uiConfig;
rootInterface()?.tenant;
me();
```
* web: Added a README with a description of the applications' "mental model," essentially an architectural description.
* web: prettier had opinions about the README
* web: Jens requested that subscription be by default, and it's the right call.
* This commit abstracts access to the object `rootInterface()?.config?` into a single accessor,
`authentikConfig`, that can be mixed into any AKElement object that requires access to it.
Since access to `rootInterface()?.config?` is _universally_ used for a single (and repetitive)
boolean check, a separate accessor has been provided that converts all calls of the form:
``` javascript
rootInterface()?.config?.capabilities.includes(CapabilitiesEnum.CanImpersonate)
```
into:
``` javascript
this.can(CapabilitiesEnum.CanImpersonate)
```
It does this via a Mixin, `WithCapabilitiesConfig`, which understands that these calls only make
sense in the context of a running, fully configured authentik instance, and that their purpose is to
inform authentik components of a user’s capabilities. The latter is why I don’t feel uncomfortable
turning a function call into a method; we should make it explicit that this is a relationship
between components.
The mixin has a single single field, `[WCC.capabilitiesConfig]`, where its association with the
upper-level configuration is made. If that syntax looks peculiar to you, good! I’ve used an explict
unique symbol as the field name; it is inaccessable an innumerable in the object list. The debugger
shows it only as:
Symbol(): {
cacheTimeout: 300
cacheTimeoutFlows: 300
cacheTimeoutPolicies: 300
cacheTimeoutReputation: 300
capabilities: (5) ['can_save_media', 'can_geo_ip', 'can_impersonate', 'can_debug', 'is_enterprise']
}
Since you can’t reference it by identity, you can’t write to it. Until every browser supports actual
private fields, this is the best we can do; it does guarantee that field name collisions are
impossible, which is a win.
The mixin takes a second optional boolean; setting this to true will cause any web component using
the mixin to automatically schedule a re-render if the capabilities list changes.
The mixin is also generic; despite the "...into a Lit-Context" in the title, the internals of the
Mixin can be replaced with anything so long as the signature of `.can()` is preserved.
Because this work builds off the work I did to give the Sidebar access to the configuration without
ad-hoc retrieval or prop-drilling, it wasn’t necessary to create a new context for it. That will be
necessary for the following:
TODO:
``` javascript
rootInterface()?.uiConfig;
rootInterface()?.tenant;
me();
```
* web: Added a README with a description of the applications' "mental model," essentially an architectural description.
* web: prettier had opinions about the README
* web: Jens requested that subscription be by default, and it's the right call.
* web: adjust RAC to point to the (now independent) Interface.
- Also, removed redundant check.
2024-01-08 18:22:52 +00:00
|
|
|
# The Theory of the authentik UI
|
|
|
|
|
|
|
|
In Peter Naur's 1985 essay [Programming as Theory
|
|
|
|
Building](https://pages.cs.wisc.edu/~remzi/Naur.pdf), programming is described as creating a mental
|
|
|
|
model of how a program _should_ run, then writing the code to test if the program _can_ run that
|
|
|
|
way.
|
|
|
|
|
|
|
|
The mental model for the authentik UI is straightforward. There are five "applications" within the
|
|
|
|
UI, each with its own base URL, router, and responsibilities, and each application needs as many as
|
|
|
|
three contexts in which to run.
|
|
|
|
|
|
|
|
The three contexts corresponds to objects in the API's `model` section, so let's use those names.
|
|
|
|
|
|
|
|
- The root `Config`. The root configuration object of the server, containing mostly caching and
|
|
|
|
error reporting information. This is misleading, however; the `Config` object contains some user
|
|
|
|
information, specifically a list of permissions the current user (or "no user") has.
|
|
|
|
- The root `CurrentTenant`. This describes the `Brand` information UIs should use, such as themes,
|
|
|
|
logos, favicon, and specific default flows for logging in, logging out, and recovering a user
|
|
|
|
password.
|
|
|
|
- The current `SessionUser`, the person logged in: username, display name, and various states.
|
|
|
|
(Note: the authentik server permits administrators to "impersonate" any other user in order to
|
|
|
|
debug their authentikation experience. If impersonation is active, the `user` field reflects that
|
|
|
|
user, but it also includes a field, `original`, with the administrator's information.)
|
|
|
|
|
|
|
|
(There is a fourth context object, Version, but its use is limited to displaying version information
|
|
|
|
and checking for upgrades. Just be aware that you will see it, but you will probably never interact
|
|
|
|
with it.)
|
|
|
|
|
|
|
|
There are five applications. Two (`loading` and `api-browser`) are trivial applications whose
|
|
|
|
insides are provided by third-party libraries (Patternfly and Rapidoc, respectively). The other
|
|
|
|
three are actual applications. The descriptions below are wholly from the view of the user's
|
|
|
|
experience:
|
|
|
|
|
|
|
|
- `Flow`: From a given URL, displays a form that requests information from the user to accomplish a
|
|
|
|
task. Some tasks require the user to be logged in, but many (such as logging in itself!)
|
|
|
|
obviously do not.
|
|
|
|
- `User`: Provides the user with access to the applications they can access, plus a few user
|
|
|
|
settings.
|
|
|
|
- `Admin`: Provides someone with super-user permissions access to the administrative functions of
|
|
|
|
the authentik server.
|
|
|
|
|
|
|
|
**Mental Model**
|
|
|
|
|
|
|
|
- Upon initialization, _every_ authentik UI application fetches `Config` and `CurrentTenant`. `User`
|
|
|
|
and `Admin` will also attempt to load the `SessionUser`; if there is none, the user is kicked out
|
|
|
|
to the `Flow` for logging into authentik itself.
|
|
|
|
- `Config`, `CurrentTenant`, and `SessionUser`, are provided by the `@goauthentik/api` application,
|
|
|
|
not by the codebase under `./web`. (Where you are now).
|
|
|
|
- `Flow`, `User`, and `Admin` are all called `Interfaces` and are found in
|
|
|
|
`./web/src/flow/FlowInterface`, `./web/src/user/UserInterface`, `./web/src/admin/AdminInterface`,
|
|
|
|
respectively.
|
|
|
|
|
|
|
|
Inside each of these you will find, in a hierarchal order:
|
|
|
|
|
|
|
|
- The context layer described above
|
|
|
|
- A theme managing layer
|
|
|
|
- The orchestration layer:
|
|
|
|
- web socket handler for server-generated events
|
|
|
|
- The router
|
|
|
|
- Individual routes for each vertical slice and its relationship to other objects:
|
|
|
|
|
|
|
|
Each slice corresponds to an object table on the server, and each slice _usually_ consists of the
|
|
|
|
following:
|
|
|
|
|
|
|
|
- A paginated collection display, usually using the `Table` foundation (found in
|
|
|
|
`./web/src/elements/Table`)
|
|
|
|
- The ability to view an individual object from the collection, which you may be able to:
|
|
|
|
- Edit
|
|
|
|
- Delete
|
|
|
|
- A form for creating a new object
|
|
|
|
- Tabs showing that object's relationship to other objects
|
|
|
|
- Interactive elements for changing or deleting those relationships, or creating new ones.
|
|
|
|
- The ability to create new objects with which to have that relationship, if they're not part of
|
|
|
|
the core objects (such as User->MFA authenticator apps, since the latter is not a "core" object
|
|
|
|
and has no tab of its own).
|
|
|
|
|
|
|
|
We are still a bit "all over the place" with respect to sub-units and common units; there are
|
|
|
|
folders `common`, `elements`, and `components`, and ideally they would be:
|
|
|
|
|
|
|
|
- `common`: non-UI related libraries all of our applications need
|
|
|
|
- `elements`: UI elements shared among multiple applications that do not need context
|
|
|
|
- `components`: UI elements shared among multiple that use one or more context
|
|
|
|
|
|
|
|
... but at the moment there are some context-sensitive elements, and some UI-related stuff in
|
|
|
|
`common`.
|
|
|
|
|
web: Add storybook (#5865)
* \#\# Details
web: replace lingui with lit/localize
\#\# Changes
This rather massive shift replaces the lingui and `t()` syntax with lit-localize, XLIFF, and the `msg()`
syntax used by lit-localize. 90% of this work was mechanized; simple perl scripts found and replaced
all uses of `t()` with the appropriate corresponding syntax for `msg()` and `msg(str())`.
The XLIFF files were auto-generated from the PO files. They have not been audited, and they should be
checked over by professional translators. The actual _strings_ have not been changed, but as this was
a mechanized change there is always the possibility of mis-translation-- not by the translator, but by
the script.
* web: revise lit/localize: fix two installation issues.
* web: revise localization
TL;DR:
- Replaced all of Lingui's `t()` syntax with `msg()` syntax.
- Mechanically (i.e with a script) converted all of the PO files to XLIFF files
- Refactored the localization code to be a bit smarter:
- the function `getBestMatchLocale` takes the locale lists and a requested locale, and returns the
first match of:
- The locale's code exactly matches the requested locale
- The locale code exactly matches the prefix of the requested locale (i.e the "en" part of "en-US")
- the locale code's prefix exactly matches the prefix of the requested locale
This function is passed to lit-locate's `loadLocale()`.
- `activateLocale()` just calls `loadLocale()` now.
- `autodetectLanguage` searches the following, and picks the first that returns a valid locale
object, before passing it to `loadLocale()`:
- The User's settings
- A `?locale=` component found in `window.location.search`
- The `window.navigator.language` field
- English
The `msg()` only runs when it's run. This seems obvious, but it means that you cannot cache
strings at load time; they must be kept inside functions that are re-run so that the `msg()` engine
can look up the strings in the preferred language of the user at that moment.
You can use thunks-of-strings if you really need them that way.
* Including the 'xliff-converter' in case anyone wants to review it.
* The xliff-converter is tagged as 'xliff-converter', but has been
deleted.
\#\# Details
- Resolves #5171
\#\# Changes
\#\#\# New Features
- Adds a "Add an Application" to the LibraryView if there are no applications and the user is an administrator.
\#\#\# Breaking Changes
- Adds breaking change which causes \<issue\>.
\#\# Checklist
- [ ] Local tests pass (`ak test authentik/`)
- [ ] The code has been formatted (`make lint-fix`)
If an API change has been made
- [ ] The API schema has been updated (`make gen-build`)
If changes to the frontend have been made
- [ ] The code has been formatted (`make web`)
- [ ] The translation files have been updated (`make i18n-extract`)
If applicable
- [ ] The documentation has been updated
- [ ] The documentation has been formatted (`make website`)
* web: fix redundant locales for zh suite.
* web: prettier pass for locale update
* web: localization moderization
Changed the names of the lit-localize commands to make it clear they're
part of the localization effort, and not just "build" and "extract".
* web: add storybook to test components
* update transifex config
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* fix package lock?
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* use build not compile
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
* web: conversion to lit-localize
The CI produced a list of problems that I hadn't caught earlier,
due to a typo ("localize build" is correct, "localize compile" is
not) I had left in package.json. They were minor and linty, but
it was still wise to fix them.
* web: replace lingui with lit/locale
This commit fixes some minor linting issues that were hidden by a typo in package.json. The
issues were not apparently problematic from a Javascript point of view, but they pointed
to sloppy thinking in the progression of types through the system, so I cleaned them
up and formalized the types from LocaleModule to AkLocale.
* web: replace lingui with lit/localize
One problem that has repeatedly come up is that localize's templates do not produce
JavaScript that conforms with our shop style. I've replaced `build-locale` with
a two-step that builds the locale *and* ensures that it conforms to the shop style
via `prettier` every time.
* web: replace lingui with lit-locale
This commit applies the most recent bundle of translations to the
new lit-locale aspect component. It also revises the algorithm
for *finding* the correct locale, replacing the complex fall-back
with some rather straightforward regular expressions.
In the case of Chinese, the fallback comes at the end of the
selection list, which may not be, er, politically valuable
(since Taiwan and Hong Kong come before, being exceptions that
need to be tested). If we need a different order for presentation,
that'll be a future feature.
* web: replace lingui with lit/locale
Well, that was embarassing.
* web: add storybook
The delta on this didn't make any sense; putting it back causes no behavioral
changes.
* web: add Storybook
Fixed a typo in the package.json that prevented the TSC check
from passing.
* web: incorporate storybook
This commit includes a number of type and definitional changes needed to make lit-analyze pass. In
most cases, it was a matter of reassuring Lit that we were using the right type and the right type
converter, or configuring the property such that it should never be called as an attribute.
The most controversial change is adding the 'no-incompatible-type-binding' to the LIT analyzer
configuration (found in `tsconfig.json`). This "routes around" lit-analyzer not doing very well
understanding that some HTML objects can have generic property types, as long as the renderer is
configured correctly.
The 'no-missing-import: off' setting is required as lit-analyzer also does not use the tsconfig
`paths` setting correctly and cannot find objects defined via aliases.
It's a shame JSON can't support comments; these should be in the tsconfig.json file directly. As it
is, I've started a README file that includes a section to record configuration decisions.
Deleted the lingui.config file as we're not using it anymore
* ignore storybook build in git
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-06-07 11:05:33 +00:00
|
|
|
# Comments
|
|
|
|
|
|
|
|
**NOTE:** The comments in this section are for specific changes to this repository that cannot be
|
|
|
|
reliably documented any other way. For the most part, they contain comments related to custom
|
|
|
|
settings in JSON files, which do not support comments.
|
|
|
|
|
|
|
|
- `tsconfig.json`:
|
|
|
|
- `compilerOptions.useDefineForClassFields: false` is required to make TSC use the "classic" form
|
|
|
|
of field definition when compiling class definitions. Storybook does not handle the ESNext
|
|
|
|
proposed definition mechanism (yet).
|
|
|
|
- `compilerOptions.plugins.ts-lit-plugin.rules.no-unknown-tag-name: "off"`: required to support
|
|
|
|
rapidoc, which exports its tag late.
|
|
|
|
- `compilerOptions.plugins.ts-lit-plugin.rules.no-missing-import: "off"`: lit-analyzer currently
|
|
|
|
does not support path aliases very well, and cannot find the definition files associated with
|
|
|
|
imports using them.
|
|
|
|
- `compilerOptions.plugins.ts-lit-plugin.rules.no-incompatible-type-binding: "warn"`: lit-analyzer
|
|
|
|
does not support generics well when parsing a subtype of `HTMLElement`. As a result, this threw
|
|
|
|
too many errors to be supportable.
|