web/admin/bugfix: Edit Stage not working. Invoking IdentificationStageForm not working (#20429)

* web/admin/bugfix: Edit Stage not working. Invoking IdentificationStageForm not working.

## What

1.  Fix the field being referenced by Flows -\> \[One Flow\] -\> StageBindings -\> \[Edit Stage\] to use the PK for the *stage*, rather than the *binding*.
2.  Added a check in `StrictUnsafe`: if the property is “wrapped” and untyped, treat it as an attribute, not a property.
3.  Edit the `ak-bound-stages-list` target attribute to be an attribute, not a property.

## Why

1.  This looks like a simple typo. To avoid this in the future, *we need tests*.
2.  `ModelForm` uses both a converter and get/set accessors to manage the pk (primary key) of the object it is being invoked to edit: the first because Django primary keys can be either strings or numbers, and the latter because we have special transactional requirements when a primary key changes. Lit’s magic for handling this creates some weirdness around JavaScript prototyping (`wrapped` becomes the only key on the object; all the other keys become delegated to a prototype object), so `hasOwn()` can’t be used; we just have to check for `wrapped` and `!type`.
3.  PKs are either strings or numbers, and ModelForm has a smart converter. There’s no need to shove the values around as properties and, in fact, that’ll break some things because there’s a working `attribute` field on ModelForm! Removing the `.` property marker both avoids this issue and makes visible exactly what item-id is being referenced.

* Forced update of package lock.  AGAIN.

* Sigh

* Sigh. Again.

* Sigh. But this time, with an empty cache.

* Prettier and its opinions.

* Clearing the cache broke relationships inside SFE. That has been updated.

* WTF, over?
This commit is contained in:
Ken Sternberg
2026-02-20 08:44:51 -08:00
committed by GitHub
parent d8f78ff653
commit ab981dec86
6 changed files with 1310 additions and 1231 deletions
+344 -307
View File
File diff suppressed because it is too large Load Diff
+954 -914
View File
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -5,17 +5,17 @@
"private": true,
"scripts": {
"build": "wireit",
"build:sfe": "npm run build -w @goauthentik/web-sfe",
"build-locales": "node scripts/build-locales.mjs",
"build-proxy": "wireit",
"build:sfe": "npm run build -w @goauthentik/web-sfe",
"bundler:watch": "node scripts/build-web.mjs --watch",
"extract-locales": "lit-localize extract",
"format": "wireit",
"lint": "eslint --fix .",
"lint-check": "eslint --max-warnings 0 .",
"lint:imports": "knip --config scripts/knip.config.ts",
"lint:lockfile": "wireit",
"lint:types": "wireit",
"lint-check": "eslint --max-warnings 0 .",
"lit-analyse": "wireit",
"precommit": "wireit",
"prettier": "prettier --cache --write -u .",
+1 -1
View File
@@ -89,7 +89,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
html` <ak-forms-modal>
${StrictUnsafe<CustomFormElementTagName>(item.stageObj?.component, {
slot: "form",
instancePk: item.pk,
instancePk: item.stageObj?.pk,
actionLabel: msg("Update"),
headline: msg(str`Update ${item.stageObj?.verboseName}`, {
id: "form.headline.update",
+1 -1
View File
@@ -272,7 +272,7 @@ export class FlowViewPage extends AKElement {
>
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-bound-stages-list .target=${this.flow.pk}> </ak-bound-stages-list>
<ak-bound-stages-list target=${this.flow.pk}> </ak-bound-stages-list>
</div>
</div>
</div>
+8 -6
View File
@@ -43,19 +43,23 @@ export const Prefix = {
export type Prefix = (typeof Prefix)[keyof typeof Prefix];
type WrappedPropertyDeclaration = PropertyDeclaration<unknown, unknown> & { wrapped?: boolean };
/**
* Given a Lit property declaration, determine the appropriate prefix for rendering the property as either a property or an attribute, based on the declaration's type and attribute configuration.
*
* @param propDeclaration The Lit property declaration to analyze.
* @returns The determined prefix for rendering the property.
*/
function resolvePrefix<T extends PropertyDeclaration<unknown, unknown>>(
propDeclaration: T,
): Prefix {
function resolvePrefix<T extends WrappedPropertyDeclaration>(propDeclaration: T): Prefix {
if (!propDeclaration.attribute) {
return Prefix.Property;
}
if ("wrapped" in propDeclaration && propDeclaration.wrapped && !propDeclaration.type) {
return Prefix.Attribute;
}
switch (propDeclaration.type) {
case String:
return Prefix.Attribute;
@@ -71,7 +75,7 @@ function resolvePrefix<T extends PropertyDeclaration<unknown, unknown>>(
* determine the appropriate name to use for rendering the property,
* taking into account any custom attribute name specified in the declaration.
*/
function resolvePropertyName<T extends PropertyDeclaration<unknown, unknown>>(
function resolvePropertyName<T extends WrappedPropertyDeclaration>(
propDeclaration: T,
prefix: Prefix,
key: string,
@@ -148,9 +152,7 @@ export function StrictUnsafe<T extends string>(
if (propDeclaration) {
const prefix = resolvePrefix(propDeclaration);
const name = resolvePropertyName(propDeclaration, prefix, propName);
filteredProps[`${prefix}${name}`] = propValue;
continue;
}