 e85d2d0096
			
		
	
	e85d2d0096
	
	
	
		
			
			* 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.
* This (temporary) change is needed to prevent the unit tests from failing.
\# What
\# Why
\# How
\# Designs
\# Test Steps
\# Other Notes
* Revert "This (temporary) change is needed to prevent the unit tests from failing."
This reverts commit dddde09be5.
* web: remove Lit syntax from always true attributes
## What
Replaces instances of `?loading=${true}` and `?loading="${true}"` with `loading`
## Why
The Lit syntax is completely unnecessary when the attribute's state is constant, and it's a few
(just a few) extra CPU cycles for Lit to process that.
More to the point, it annoys me.
## How
```
$ perl -pi.bak -e 's/\?loading=\$\{true\}/loading/' $(rg -l '\?loading=\$\{true\}')
$ find . -name '*.bak' -exec rm {} \;
$ perl -pi.bak -e 's/\?loading="\$\{true\}"/loading/' $(rg -l '\?loading="\$\{true\}"')
$ find . -name '*.bak' -exec rm {} \;
```
* Prettier had opinions
* web: move optional textual information out of attributes and into slots
## What
Replaces instances of:
```
<ak-empty-state header=${msg(...)}></ak-empty-state>
```
with
```
<ak-empty-state><span slot="header">${msg(...)}</span></ak-empty-state>
```
## Why
1. It's correct.
2. It lets us elide the decorations for any slots we aren't using.
3. It's preparation for moving to Patternfly 5
4. It annoyed me.
## How
Since we already have Patternfly Elements installed, we have access to the PFE-Core, which has the
unbelievable useful `SlotsController`.  Using it, I created a conditional render template that will
only put in the header, body, and primary slots if there is something in the lightDOM requesting
those slots.  The conditional template will still put the spinner in if the header is not provided
but the loading state is true.
I then had to edit all the places where this is used. For about 30 of them, this script sufficed:
```
perl -pi.bak -e 's/header="?(\$\{msg\([^)]+\)\})"?>/><span slot="header">\1<\/span>/' \
     $(rg -l `<ak-empty-state[^>]header')
```
The other six had to be done by hand.  I have tested a handful of the automatic ones, and all of the
ones that were edited manually.  I'm pleasantly surprised that the textual rules [are inherited by
the slots as expected](https://htmlwithsuperpowers.netlify.app/styling/inheritable.htm).
		
	
		
			
				
	
	
		
			165 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			165 lines
		
	
	
		
			6.1 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
| import { ApplicationWizardStep } from "@goauthentik/admin/applications/wizard/ApplicationWizardStep.js";
 | |
| import "@goauthentik/admin/applications/wizard/ak-wizard-title.js";
 | |
| import "@goauthentik/components/ak-radio-input";
 | |
| import "@goauthentik/components/ak-slug-input";
 | |
| import "@goauthentik/components/ak-status-label";
 | |
| import "@goauthentik/components/ak-switch-input";
 | |
| import "@goauthentik/components/ak-text-input";
 | |
| import { type WizardButton } from "@goauthentik/components/ak-wizard/types";
 | |
| import "@goauthentik/elements/ak-table/ak-select-table.js";
 | |
| import { SelectTable } from "@goauthentik/elements/ak-table/ak-select-table.js";
 | |
| import "@goauthentik/elements/forms/FormGroup";
 | |
| import "@goauthentik/elements/forms/HorizontalFormElement";
 | |
| import { P, match } from "ts-pattern";
 | |
| 
 | |
| import { msg, str } from "@lit/localize";
 | |
| import { css, html } from "lit";
 | |
| import { customElement, query } from "lit/decorators.js";
 | |
| 
 | |
| import PFCard from "@patternfly/patternfly/components/Card/card.css";
 | |
| 
 | |
| import { makeEditButton } from "./bindings/ak-application-wizard-bindings-edit-button.js";
 | |
| import "./bindings/ak-application-wizard-bindings-toolbar.js";
 | |
| 
 | |
| const COLUMNS = [
 | |
|     [msg("Order"), "order"],
 | |
|     [msg("Binding")],
 | |
|     [msg("Enabled"), "enabled"],
 | |
|     [msg("Timeout"), "timeout"],
 | |
|     [msg("Actions")],
 | |
| ];
 | |
| 
 | |
| @customElement("ak-application-wizard-bindings-step")
 | |
| export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
 | |
|     label = msg("Configure Bindings");
 | |
| 
 | |
|     get buttons(): WizardButton[] {
 | |
|         return [
 | |
|             { kind: "next", destination: "submit" },
 | |
|             { kind: "back", destination: "provider" },
 | |
|             { kind: "cancel" },
 | |
|         ];
 | |
|     }
 | |
| 
 | |
|     @query("ak-select-table")
 | |
|     selectTable!: SelectTable;
 | |
| 
 | |
|     static get styles() {
 | |
|         return super.styles.concat(
 | |
|             PFCard,
 | |
|             css`
 | |
|                 .pf-c-card {
 | |
|                     margin-top: 1em;
 | |
|                 }
 | |
|             `,
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     get bindingsAsColumns() {
 | |
|         return this.wizard.bindings.map((binding, index) => {
 | |
|             const { order, enabled, timeout } = binding;
 | |
|             const isSet = P.union(P.string.minLength(1), P.number);
 | |
|             const policy = match(binding)
 | |
|                 .with({ policy: isSet }, (v) => msg(str`Policy ${v.policyObj?.name}`))
 | |
|                 .with({ group: isSet }, (v) => msg(str`Group ${v.groupObj?.name}`))
 | |
|                 .with({ user: isSet }, (v) => msg(str`User ${v.userObj?.name}`))
 | |
|                 .otherwise(() => msg("-"));
 | |
| 
 | |
|             return {
 | |
|                 key: index,
 | |
|                 content: [
 | |
|                     order,
 | |
|                     policy,
 | |
|                     html`<ak-status-label type="warning" ?good=${enabled}></ak-status-label>`,
 | |
|                     timeout,
 | |
|                     makeEditButton(msg("Edit"), index, (ev: CustomEvent<number>) =>
 | |
|                         this.onBindingEvent(ev.detail),
 | |
|                     ),
 | |
|                 ],
 | |
|             };
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     // TODO Fix those dispatches so that we handle them here, in this component, and *choose* how to
 | |
|     // forward them.
 | |
|     onBindingEvent(binding?: number) {
 | |
|         this.handleUpdate({ currentBinding: binding ?? -1 }, "edit-binding", {
 | |
|             enable: "edit-binding",
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     onDeleteBindings() {
 | |
|         const toDelete = this.selectTable
 | |
|             .json()
 | |
|             .map((i) => (typeof i === "string" ? parseInt(i, 10) : i));
 | |
|         const bindings = this.wizard.bindings.filter((binding, index) => !toDelete.includes(index));
 | |
|         this.handleUpdate({ bindings }, "bindings");
 | |
|     }
 | |
| 
 | |
|     renderEmptyCollection() {
 | |
|         return html`<ak-wizard-title
 | |
|                 >${msg("Configure Policy/User/Group Bindings")}</ak-wizard-title
 | |
|             >
 | |
|             <h6 class="pf-c-title pf-m-md">
 | |
|                 ${msg("These policies control which users can access this application.")}
 | |
|             </h6>
 | |
|             <div class="pf-c-card">
 | |
|                 <ak-application-wizard-bindings-toolbar
 | |
|                     @clickNew=${() => this.onBindingEvent()}
 | |
|                     @clickDelete=${() => this.onDeleteBindings()}
 | |
|                 ></ak-application-wizard-bindings-toolbar>
 | |
|                 <ak-select-table
 | |
|                     multiple
 | |
|                     id="bindings"
 | |
|                     order="order"
 | |
|                     .columns=${COLUMNS}
 | |
|                     .content=${[]}
 | |
|                 ></ak-select-table>
 | |
|                 <ak-empty-state icon="pf-icon-module"
 | |
|                     ><span slot="header">${msg("No bound policies.")} </span>
 | |
|                     <div slot="body">${msg("No policies are currently bound to this object.")}</div>
 | |
|                     <div slot="primary">
 | |
|                         <button
 | |
|                             @click=${() => this.onBindingEvent()}
 | |
|                             class="pf-c-button pf-m-primary"
 | |
|                         >
 | |
|                             ${msg("Bind policy/group/user")}
 | |
|                         </button>
 | |
|                     </div>
 | |
|                 </ak-empty-state>
 | |
|             </div>`;
 | |
|     }
 | |
| 
 | |
|     renderCollection() {
 | |
|         return html` <ak-wizard-title>${msg("Configure Policy Bindings")}</ak-wizard-title>
 | |
|             <h6 class="pf-c-title pf-m-md">
 | |
|                 ${msg("These policies control which users can access this application.")}
 | |
|             </h6>
 | |
|             <ak-application-wizard-bindings-toolbar
 | |
|                 @clickNew=${() => this.onBindingEvent()}
 | |
|                 @clickDelete=${() => this.onDeleteBindings()}
 | |
|                 ?can-delete=${this.wizard.bindings.length > 0}
 | |
|             ></ak-application-wizard-bindings-toolbar>
 | |
|             <ak-select-table
 | |
|                 multiple
 | |
|                 id="bindings"
 | |
|                 order="order"
 | |
|                 .columns=${COLUMNS}
 | |
|                 .content=${this.bindingsAsColumns}
 | |
|             ></ak-select-table>`;
 | |
|     }
 | |
| 
 | |
|     renderMain() {
 | |
|         if ((this.wizard.bindings ?? []).length === 0) {
 | |
|             return this.renderEmptyCollection();
 | |
|         }
 | |
|         return this.renderCollection();
 | |
|     }
 | |
| }
 | |
| 
 | |
| declare global {
 | |
|     interface HTMLElementTagNameMap {
 | |
|         "ak-application-wizard-applications-step": ApplicationWizardBindingsStep;
 | |
|     }
 | |
| }
 |