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.
This commit is contained in:
@ -3,6 +3,92 @@
|
||||
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.
|
||||
|
||||
# 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`.
|
||||
|
||||
# Comments
|
||||
|
||||
**NOTE:** The comments in this section are for specific changes to this repository that cannot be
|
||||
|
Reference in New Issue
Block a user