mirror of
https://github.com/goauthentik/authentik
synced 2026-04-25 17:15:26 +02:00
Compare commits
19 Commits
0459568a96
...
explicit-m
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07aa1f99f6 | ||
|
|
91f4777aa4 | ||
|
|
1226e60c73 | ||
|
|
5fa3dd95be | ||
|
|
9c588565df | ||
|
|
fa43b4b421 | ||
|
|
69fbd4f425 | ||
|
|
d44f33068f | ||
|
|
26de7a21c7 | ||
|
|
234530a8c2 | ||
|
|
1f01c722ba | ||
|
|
36e03443f6 | ||
|
|
3d734f1433 | ||
|
|
5dbee033dc | ||
|
|
d3abff1f5a | ||
|
|
823a3b312b | ||
|
|
fae89e1f74 | ||
|
|
6a218d818a | ||
|
|
a821ea6074 |
338
web/codemods/explicit-member-accessibility.mjs
Normal file
338
web/codemods/explicit-member-accessibility.mjs
Normal file
@@ -0,0 +1,338 @@
|
||||
/* eslint-disable max-depth */
|
||||
/**
|
||||
* @file TypeScript codemod to add explicit member accessibility modifiers to class methods and properties.
|
||||
*
|
||||
* Given a class like this:
|
||||
*
|
||||
* ```ts
|
||||
* class Foo {
|
||||
* static styles = [
|
||||
* css`
|
||||
* :host {
|
||||
* display: block;
|
||||
* }
|
||||
* `,
|
||||
* ];
|
||||
*
|
||||
* @property()
|
||||
* name?: string;
|
||||
*
|
||||
* @state()
|
||||
* isLoading = false;
|
||||
*
|
||||
* renderHeader() {
|
||||
* return html`<h1>Hello world</h1>`;
|
||||
* }
|
||||
*
|
||||
* render() {
|
||||
* return html`
|
||||
* ${this.renderHeader()}
|
||||
* <div>Content</div>
|
||||
* `;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* This codemod will transform it to this:
|
||||
*
|
||||
* ```ts
|
||||
* class Foo {
|
||||
* public static styles = [
|
||||
* css`
|
||||
* :host {
|
||||
* display: block;
|
||||
* }
|
||||
* `,
|
||||
* ];
|
||||
*
|
||||
* @property()
|
||||
* public name?: string;
|
||||
*
|
||||
* @state()
|
||||
* protected isLoading = false;
|
||||
*
|
||||
* protected renderHeader() {
|
||||
* return html`<h1>Hello world</h1>`;
|
||||
* }
|
||||
*
|
||||
* public render() {
|
||||
* return html`
|
||||
* ${this.renderHeader()}
|
||||
* <div>Content</div>
|
||||
* `;
|
||||
* }
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Rules:
|
||||
* - Any method which is `render` is given an explicit `public` accessibility modifier.
|
||||
* - Any method which begins with `render` is given an explicit `protected` accessibility modifier.
|
||||
* - Any property which is static is given an explicit `public` accessibility modifier.
|
||||
* - Any property with `@property` decorator is given an explicit `public` accessibility modifier.
|
||||
* - Any property with `@state` decorator is given an explicit `protected` accessibility modifier.
|
||||
* - Methods and properties which already have an accessibility modifier are left alone.
|
||||
* - Private fields (starting with #) are skipped.
|
||||
*/
|
||||
|
||||
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
||||
import path from "node:path";
|
||||
|
||||
import ts from "typescript";
|
||||
|
||||
/**
|
||||
* @param {ts.NodeArray<ts.ModifierLike>} modifiers
|
||||
*/
|
||||
function hasAccessibilityModifier(modifiers) {
|
||||
if (!modifiers) return false;
|
||||
|
||||
return modifiers.some(
|
||||
(modifier) =>
|
||||
modifier.kind === ts.SyntaxKind.PublicKeyword ||
|
||||
modifier.kind === ts.SyntaxKind.PrivateKeyword ||
|
||||
modifier.kind === ts.SyntaxKind.ProtectedKeyword,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {ts.NodeArray<ts.ModifierLike>} [modifiers]
|
||||
* @returns
|
||||
*/
|
||||
function hasStaticModifier(modifiers) {
|
||||
if (!modifiers) return false;
|
||||
return modifiers.some((modifier) => modifier.kind === ts.SyntaxKind.StaticKeyword);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property has a decorator.
|
||||
* @param {ts.PropertyDeclaration} member
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function isDecorated(member) {
|
||||
if (!member.modifiers) return false;
|
||||
|
||||
return member.modifiers.some((modifier) => {
|
||||
if (!ts.isDecorator(modifier)) return false;
|
||||
|
||||
// Handle simple decorator like @property
|
||||
if (ts.isIdentifier(modifier.expression)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Handle decorator with call like @property() or @property({...})
|
||||
return (
|
||||
ts.isCallExpression(modifier.expression) &&
|
||||
ts.isIdentifier(modifier.expression.expression)
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property has a specific decorator
|
||||
* @param {ts.PropertyDeclaration} member
|
||||
* @param {string} decoratorName
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function hasDecorator(member, decoratorName) {
|
||||
if (!member.modifiers) return false;
|
||||
|
||||
return member.modifiers.some((modifier) => {
|
||||
if (!ts.isDecorator(modifier)) return false;
|
||||
|
||||
// Handle simple decorator like @property
|
||||
if (ts.isIdentifier(modifier.expression)) {
|
||||
return modifier.expression.text === decoratorName;
|
||||
}
|
||||
|
||||
// Handle decorator with call like @property() or @property({...})
|
||||
if (
|
||||
ts.isCallExpression(modifier.expression) &&
|
||||
ts.isIdentifier(modifier.expression.expression)
|
||||
) {
|
||||
return modifier.expression.expression.text === decoratorName;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
const PublicMethodNames = new Set([
|
||||
"render",
|
||||
"connectedCallback",
|
||||
"disconnectedCallback",
|
||||
"firstUpdated",
|
||||
"updated",
|
||||
"requestUpdate",
|
||||
"willUpdate",
|
||||
"performUpdate",
|
||||
"adoptedCallback",
|
||||
"attributeChangedCallback",
|
||||
]);
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} filePath
|
||||
* @returns {boolean}
|
||||
*/
|
||||
function processFile(filePath) {
|
||||
const source = readFileSync(filePath, "utf-8");
|
||||
const sourceFile = ts.createSourceFile(filePath, source, ts.ScriptTarget.Latest, true);
|
||||
|
||||
/**
|
||||
* @type {{ pos: number; text: string; }[]}
|
||||
*/
|
||||
const changes = [];
|
||||
|
||||
/**
|
||||
* @param {ts.Node} node
|
||||
*/
|
||||
function collectChanges(node) {
|
||||
if (ts.isClassDeclaration(node)) {
|
||||
for (const member of node.members) {
|
||||
if (ts.isMethodDeclaration(member) || ts.isPropertyDeclaration(member)) {
|
||||
if (member.modifiers && hasAccessibilityModifier(member.modifiers)) {
|
||||
continue; // Already has accessibility modifier
|
||||
}
|
||||
|
||||
let accessibilityKeyword = null;
|
||||
|
||||
if (
|
||||
ts.isMethodDeclaration(member) &&
|
||||
member.name &&
|
||||
ts.isIdentifier(member.name)
|
||||
) {
|
||||
const methodName = member.name.text;
|
||||
|
||||
if (PublicMethodNames.has(methodName)) {
|
||||
accessibilityKeyword = "public ";
|
||||
} else if (
|
||||
methodName.startsWith("render") ||
|
||||
methodName.startsWith("on") ||
|
||||
methodName.startsWith("handle") ||
|
||||
methodName.includes("Handler")
|
||||
) {
|
||||
accessibilityKeyword = "protected ";
|
||||
}
|
||||
} else if (ts.isPropertyDeclaration(member)) {
|
||||
// Skip private fields (starting with #)
|
||||
if (member.name.getText().includes("#")) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Static properties get public modifier
|
||||
if (hasStaticModifier(member.modifiers)) {
|
||||
accessibilityKeyword = "public ";
|
||||
}
|
||||
// Properties with @property decorator get public modifier
|
||||
else if (hasDecorator(member, "property")) {
|
||||
accessibilityKeyword = "public ";
|
||||
}
|
||||
// Properties with @state decorator get protected modifier
|
||||
else if (hasDecorator(member, "state")) {
|
||||
accessibilityKeyword = "protected ";
|
||||
} else if (isDecorated(member)) {
|
||||
accessibilityKeyword = "protected ";
|
||||
}
|
||||
}
|
||||
|
||||
if (accessibilityKeyword) {
|
||||
// Find the position where we should insert the accessibility modifier
|
||||
// For properties with decorators, we need to insert after the decorators
|
||||
let insertPos = member.getStart(sourceFile);
|
||||
|
||||
// If the member has decorators, find the position after the last decorator
|
||||
if (
|
||||
member.modifiers &&
|
||||
member.modifiers.some((modifier) => ts.isDecorator(modifier))
|
||||
) {
|
||||
const decorators = member.modifiers.filter((modifier) =>
|
||||
ts.isDecorator(modifier),
|
||||
);
|
||||
const lastDecorator = decorators[decorators.length - 1];
|
||||
insertPos = lastDecorator.getEnd();
|
||||
|
||||
// Find the next non-whitespace character position
|
||||
const sourceText = sourceFile.getFullText();
|
||||
while (
|
||||
insertPos < sourceText.length &&
|
||||
/\s/.test(sourceText[insertPos])
|
||||
) {
|
||||
insertPos++;
|
||||
}
|
||||
}
|
||||
|
||||
changes.push({
|
||||
pos: insertPos,
|
||||
text: accessibilityKeyword,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ts.forEachChild(node, collectChanges);
|
||||
}
|
||||
|
||||
collectChanges(sourceFile);
|
||||
|
||||
if (changes.length === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sort changes by position in reverse order so we can apply them without affecting positions
|
||||
changes.sort((a, b) => b.pos - a.pos);
|
||||
|
||||
let result = source;
|
||||
for (const change of changes) {
|
||||
result = result.slice(0, change.pos) + change.text + result.slice(change.pos);
|
||||
}
|
||||
|
||||
writeFileSync(filePath, result, "utf-8");
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Updated: ${filePath}`);
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} dir
|
||||
* @param {string[]} files
|
||||
* @returns {string[]}
|
||||
*/
|
||||
function findTypeScriptFiles(dir = "src", files = []) {
|
||||
const entries = readdirSync(dir, { withFileTypes: true });
|
||||
|
||||
for (const entry of entries) {
|
||||
const fullPath = path.join(dir, entry.name);
|
||||
|
||||
if (entry.isDirectory()) {
|
||||
findTypeScriptFiles(fullPath, files);
|
||||
} else if (
|
||||
entry.name.endsWith(".ts") &&
|
||||
!entry.name.endsWith(".d.ts") &&
|
||||
!entry.name.endsWith(".test.ts") &&
|
||||
!entry.name.endsWith(".spec.ts") &&
|
||||
!entry.name.endsWith(".stories.ts")
|
||||
) {
|
||||
files.push(fullPath);
|
||||
}
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
function main() {
|
||||
const files = findTypeScriptFiles();
|
||||
let changedFiles = 0;
|
||||
|
||||
for (const file of files) {
|
||||
if (processFile(file)) {
|
||||
changedFiles++;
|
||||
}
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
console.log(`Processed ${files.length} files, updated ${changedFiles} files`);
|
||||
}
|
||||
|
||||
main();
|
||||
@@ -29,6 +29,12 @@ export default tseslint.config(
|
||||
},
|
||||
files: ["packages/**/*"],
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
"@typescript-eslint/explicit-member-accessibility": "warn",
|
||||
},
|
||||
files: ["src/**/*.{ts,tsx,mts,cts}"],
|
||||
},
|
||||
{
|
||||
rules: {
|
||||
"no-void": "off",
|
||||
|
||||
@@ -170,7 +170,7 @@ class Stage<T extends FlowInfoChallenge> {
|
||||
const IS_INVALID = "is-invalid";
|
||||
|
||||
class IdentificationStage extends Stage<IdentificationChallenge> {
|
||||
render() {
|
||||
override render() {
|
||||
this.html(`
|
||||
<form id="ident-form">
|
||||
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
||||
@@ -206,7 +206,7 @@ class IdentificationStage extends Stage<IdentificationChallenge> {
|
||||
}
|
||||
|
||||
class PasswordStage extends Stage<PasswordChallenge> {
|
||||
render() {
|
||||
override render() {
|
||||
this.html(`
|
||||
<form id="password-form">
|
||||
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
||||
@@ -230,13 +230,13 @@ class PasswordStage extends Stage<PasswordChallenge> {
|
||||
}
|
||||
|
||||
class RedirectStage extends Stage<RedirectChallenge> {
|
||||
render() {
|
||||
override render() {
|
||||
window.location.assign(this.challenge.to);
|
||||
}
|
||||
}
|
||||
|
||||
class AutosubmitStage extends Stage<AutosubmitChallenge> {
|
||||
render() {
|
||||
override render() {
|
||||
this.html(`
|
||||
<form id="autosubmit-form" action="${this.challenge.url}" method="POST">
|
||||
<img class="mb-4 brand-icon" src="${ak().brand.branding_logo}" alt="">
|
||||
@@ -403,7 +403,7 @@ class AuthenticatorValidateStage extends Stage<AuthenticatorValidationChallenge>
|
||||
};
|
||||
}
|
||||
|
||||
render() {
|
||||
override render() {
|
||||
if (this.challenge.deviceChallenges.length === 1) {
|
||||
this.deviceChallenge = this.challenge.deviceChallenges[0];
|
||||
}
|
||||
|
||||
@@ -19,7 +19,7 @@ import PFAbout from "@patternfly/patternfly/components/AboutModalBox/about-modal
|
||||
|
||||
@customElement("ak-about-modal")
|
||||
export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton)) {
|
||||
static styles = [
|
||||
public static styles = [
|
||||
...ModalButton.styles,
|
||||
PFAbout,
|
||||
css`
|
||||
@@ -29,7 +29,7 @@ export class AboutModal extends WithLicenseSummary(WithBrandConfig(ModalButton))
|
||||
`,
|
||||
];
|
||||
|
||||
async getAboutEntries(): Promise<[string, string | TemplateResult][]> {
|
||||
protected async getAboutEntries(): Promise<[string, string | TemplateResult][]> {
|
||||
const status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
const version = await new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
let build: string | TemplateResult = msg("Release");
|
||||
|
||||
@@ -79,7 +79,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
|
||||
//#region Styles
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
@@ -126,7 +126,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
|
||||
//#region Lifecycle
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
configureSentry(true);
|
||||
|
||||
super();
|
||||
@@ -137,7 +137,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
this.sidebarOpen = this.#sidebarMatcher.matches;
|
||||
}
|
||||
|
||||
public connectedCallback() {
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
|
||||
window.addEventListener(EVENT_NOTIFICATION_DRAWER_TOGGLE, () => {
|
||||
@@ -159,14 +159,14 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
});
|
||||
}
|
||||
|
||||
public disconnectedCallback(): void {
|
||||
public override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
this.#sidebarMatcher.removeEventListener("change", this.#sidebarMediaQueryListener);
|
||||
|
||||
WebsocketClient.close();
|
||||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
public override async firstUpdated(): Promise<void> {
|
||||
me().then((session) => {
|
||||
this.user = session;
|
||||
|
||||
@@ -181,7 +181,7 @@ export class AdminInterface extends WithCapabilitiesConfig(AuthenticatedInterfac
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
const sidebarClasses = {
|
||||
"pf-c-page__sidebar": true,
|
||||
"pf-m-light": this.activeTheme === UiThemeEnum.Light,
|
||||
|
||||
@@ -22,9 +22,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-admin-debug-page")
|
||||
export class DebugPage extends AKElement {
|
||||
static styles: CSSResult[] = [PFBase, PFCard, PFPage, PFGrid, PFButton];
|
||||
public static override styles: CSSResult[] = [PFBase, PFCard, PFPage, PFGrid, PFButton];
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`
|
||||
<ak-page-header icon="pf-icon pf-icon-user" header="Debug"> </ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
|
||||
@@ -37,7 +37,7 @@ const AdminOverviewBase = WithLicenseSummary(AKElement);
|
||||
|
||||
@customElement("ak-admin-overview")
|
||||
export class AdminOverviewPage extends AdminOverviewBase {
|
||||
static styles: CSSResult[] = [
|
||||
public static styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFGrid,
|
||||
PFPage,
|
||||
@@ -63,7 +63,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
`,
|
||||
];
|
||||
|
||||
quickActions: QuickAction[] = [
|
||||
protected quickActions: QuickAction[] = [
|
||||
[msg("Create a new application"), paramURL("/core/applications", { createWizard: true })],
|
||||
[msg("Check the logs"), paramURL("/events/log")],
|
||||
[msg("Explore integrations"), "https://goauthentik.io/integrations/", true],
|
||||
@@ -76,13 +76,13 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
];
|
||||
|
||||
@state()
|
||||
user?: SessionUser;
|
||||
protected user?: SessionUser;
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
public override async firstUpdated(): Promise<void> {
|
||||
this.user = await me();
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
const username = this.user?.user.name || this.user?.user.username;
|
||||
|
||||
return html`<ak-page-header
|
||||
@@ -154,7 +154,7 @@ export class AdminOverviewPage extends AdminOverviewBase {
|
||||
</section>`;
|
||||
}
|
||||
|
||||
renderCards() {
|
||||
protected renderCards() {
|
||||
const isEnterprise = this.hasEnterpriseLicense;
|
||||
const classes = {
|
||||
"card-container": true,
|
||||
|
||||
@@ -18,7 +18,7 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
|
||||
@customElement("ak-admin-dashboard-users")
|
||||
export class DashboardUserPage extends AKElement {
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFGrid,
|
||||
PFPage,
|
||||
PFContent,
|
||||
@@ -34,7 +34,7 @@ export class DashboardUserPage extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<ak-page-header icon="pf-icon pf-icon-user" header=${msg("User Statistics")}>
|
||||
</ak-page-header>
|
||||
<section class="pf-c-page__main-section">
|
||||
|
||||
@@ -25,21 +25,19 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-system-tasks")
|
||||
export class SystemTasksPage extends AKElement {
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFBase,
|
||||
PFList,
|
||||
PFBanner,
|
||||
PFPage,
|
||||
PFContent,
|
||||
PFButton,
|
||||
PFDescriptionList,
|
||||
PFGrid,
|
||||
PFCard,
|
||||
];
|
||||
}
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFList,
|
||||
PFBanner,
|
||||
PFPage,
|
||||
PFContent,
|
||||
PFButton,
|
||||
PFDescriptionList,
|
||||
PFGrid,
|
||||
PFCard,
|
||||
];
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-automation"
|
||||
header=${msg("System Tasks")}
|
||||
|
||||
@@ -15,11 +15,11 @@ import PFTable from "@patternfly/patternfly/components/Table/table.css";
|
||||
@customElement("ak-top-applications-table")
|
||||
export class TopApplicationsTable extends AKElement {
|
||||
@property({ attribute: false })
|
||||
topN?: EventTopPerUser[];
|
||||
public topN?: EventTopPerUser[];
|
||||
|
||||
static styles: CSSResult[] = [PFTable];
|
||||
public static override styles: CSSResult[] = [PFTable];
|
||||
|
||||
firstUpdated(): void {
|
||||
public override firstUpdated(): void {
|
||||
new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsEventsTopPerUserList({
|
||||
action: "authorize_application",
|
||||
@@ -30,7 +30,7 @@ export class TopApplicationsTable extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
renderRow(event: EventTopPerUser): TemplateResult {
|
||||
protected renderRow(event: EventTopPerUser): TemplateResult {
|
||||
return html`<tr role="row">
|
||||
<td role="cell">${event.application.name}</td>
|
||||
<td role="cell">${event.countedEvents}</td>
|
||||
@@ -43,7 +43,7 @@ export class TopApplicationsTable extends AKElement {
|
||||
</tr>`;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<table class="pf-c-table pf-m-compact" role="grid">
|
||||
<thead>
|
||||
<tr role="row">
|
||||
|
||||
@@ -22,7 +22,7 @@ export interface AdminStatus {
|
||||
export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
// Current data value state
|
||||
@state()
|
||||
value?: T;
|
||||
public value?: T;
|
||||
|
||||
// Current status state derived from value
|
||||
@state()
|
||||
@@ -33,28 +33,27 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
protected error?: APIError;
|
||||
|
||||
// Abstract methods to be implemented by subclasses
|
||||
abstract getPrimaryValue(): Promise<T>;
|
||||
abstract getStatus(value: T): Promise<AdminStatus>;
|
||||
protected abstract getPrimaryValue(): Promise<T>;
|
||||
|
||||
constructor() {
|
||||
protected abstract getStatus(value: T): Promise<AdminStatus>;
|
||||
|
||||
public constructor() {
|
||||
super();
|
||||
// Proper binding for event handler
|
||||
this.fetchData = this.fetchData.bind(this);
|
||||
// Register refresh event listener
|
||||
this.addEventListener(EVENT_REFRESH, this.fetchData);
|
||||
this.addEventListener(EVENT_REFRESH, this.#fetchData);
|
||||
}
|
||||
|
||||
// Lifecycle method: Called when component is added to DOM
|
||||
connectedCallback(): void {
|
||||
public override connectedCallback(): void {
|
||||
super.connectedCallback();
|
||||
// Initial data fetch
|
||||
this.fetchData();
|
||||
this.#fetchData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch primary data and handle errors
|
||||
*/
|
||||
private fetchData() {
|
||||
#fetchData = () => {
|
||||
this.getPrimaryValue()
|
||||
.then((value: T) => {
|
||||
this.value = value; // Triggers shouldUpdate
|
||||
@@ -64,7 +63,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
this.status = undefined;
|
||||
this.error = await parseAPIResponseError(error);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Lit lifecycle method: Determine if component should update
|
||||
@@ -72,7 +71,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
* @param changed - Map of changed properties
|
||||
* @returns boolean indicating if update should proceed
|
||||
*/
|
||||
shouldUpdate(changed: PropertyValues<this>) {
|
||||
public override shouldUpdate(changed: PropertyValues<this>) {
|
||||
if (changed.has("value") && this.value !== undefined) {
|
||||
// When value changes, fetch new status
|
||||
this.getStatus(this.value)
|
||||
@@ -106,7 +105,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
* @param status - AdminStatus object containing icon and message
|
||||
* @returns TemplateResult for status display
|
||||
*/
|
||||
private renderStatus(status: AdminStatus): SlottedTemplateResult {
|
||||
protected renderStatus(status: AdminStatus): SlottedTemplateResult {
|
||||
return html`
|
||||
<p><i class="${status.icon}"></i> ${this.renderValue()}</p>
|
||||
${status.message ? html`<p class="subtext">${status.message}</p>` : nothing}
|
||||
@@ -119,7 +118,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
* @param error - Error message to display
|
||||
* @returns TemplateResult for error display
|
||||
*/
|
||||
private renderError(error: string): SlottedTemplateResult {
|
||||
protected renderError(error: string): SlottedTemplateResult {
|
||||
return html`
|
||||
<p><i aria-hidden="true" class="fa fa-times"></i> ${msg("Failed to fetch")}</p>
|
||||
<p class="subtext">${error}</p>
|
||||
@@ -131,7 +130,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
*
|
||||
* @returns TemplateResult for loading spinner
|
||||
*/
|
||||
private renderLoading(): SlottedTemplateResult {
|
||||
protected renderLoading(): SlottedTemplateResult {
|
||||
return html`<ak-spinner size="${PFSize.Large}"></ak-spinner>`;
|
||||
}
|
||||
|
||||
@@ -140,7 +139,7 @@ export abstract class AdminStatusCard<T> extends AggregateCard {
|
||||
*
|
||||
* @returns TemplateResult for current component state
|
||||
*/
|
||||
renderInner(): SlottedTemplateResult {
|
||||
protected override renderInner(): SlottedTemplateResult {
|
||||
return html`
|
||||
<p class="center-value">
|
||||
${
|
||||
|
||||
@@ -12,21 +12,21 @@ type StatusContent = { icon: string; message: TemplateResult };
|
||||
|
||||
@customElement("ak-admin-fips-status-system")
|
||||
export class FipsStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
icon = "pf-icon pf-icon-server";
|
||||
public override icon = "pf-icon pf-icon-server";
|
||||
|
||||
@state()
|
||||
statusSummary?: string;
|
||||
protected statusSummary?: string;
|
||||
|
||||
async getPrimaryValue(): Promise<SystemInfo> {
|
||||
protected async getPrimaryValue(): Promise<SystemInfo> {
|
||||
return await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
}
|
||||
|
||||
setStatus(summary: string, content: StatusContent): Promise<AdminStatus> {
|
||||
protected setStatus(summary: string, content: StatusContent): Promise<AdminStatus> {
|
||||
this.statusSummary = summary;
|
||||
return Promise.resolve<AdminStatus>(content);
|
||||
}
|
||||
|
||||
getStatus(value: SystemInfo): Promise<AdminStatus> {
|
||||
protected getStatus(value: SystemInfo): Promise<AdminStatus> {
|
||||
return value.runtime.opensslFipsEnabled
|
||||
? this.setStatus(msg("OK"), {
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
@@ -38,11 +38,11 @@ export class FipsStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
});
|
||||
}
|
||||
|
||||
renderHeader(): TemplateResult {
|
||||
protected override renderHeader(): TemplateResult {
|
||||
return html`${msg("FIPS Status")}`;
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
protected override renderValue(): TemplateResult {
|
||||
return html`${this.statusSummary}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,19 +25,19 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
@customElement("ak-recent-events")
|
||||
export class RecentEventsCard extends Table<Event> {
|
||||
@property()
|
||||
order = "-created";
|
||||
public override order = "-created";
|
||||
|
||||
@property({ type: Number })
|
||||
pageSize = 10;
|
||||
public pageSize = 10;
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Event>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Event>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
pageSize: this.pageSize,
|
||||
});
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
PFCard,
|
||||
css`
|
||||
@@ -52,7 +52,7 @@ export class RecentEventsCard extends Table<Event> {
|
||||
`,
|
||||
];
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Action"), "action"),
|
||||
new TableColumn(msg("User"), "user"),
|
||||
@@ -62,13 +62,13 @@ export class RecentEventsCard extends Table<Event> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`<div class="pf-c-card__title">
|
||||
<i class="pf-icon pf-icon-catalog"></i> ${msg("Recent events")}
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: EventWithContext): SlottedTemplateResult[] {
|
||||
protected row(item: EventWithContext): SlottedTemplateResult[] {
|
||||
return [
|
||||
html`<div><a href="${`#/events/log/${item.pk}`}">${actionToLabel(item.action)}</a></div>
|
||||
<small>${item.app}</small>`,
|
||||
@@ -81,7 +81,7 @@ export class RecentEventsCard extends Table<Event> {
|
||||
];
|
||||
}
|
||||
|
||||
renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
|
||||
protected override renderEmpty(inner?: SlottedTemplateResult): TemplateResult {
|
||||
if (this.error) {
|
||||
return super.renderEmpty(inner);
|
||||
}
|
||||
|
||||
@@ -12,14 +12,14 @@ import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-system")
|
||||
export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
now?: Date;
|
||||
protected now?: Date;
|
||||
|
||||
icon = "pf-icon pf-icon-server";
|
||||
public override icon = "pf-icon pf-icon-server";
|
||||
|
||||
@state()
|
||||
statusSummary?: string;
|
||||
protected statusSummary?: string;
|
||||
|
||||
async getPrimaryValue(): Promise<SystemInfo> {
|
||||
protected async getPrimaryValue(): Promise<SystemInfo> {
|
||||
this.now = new Date();
|
||||
let status = await new AdminApi(DEFAULT_CONFIG).adminSystemRetrieve();
|
||||
if (
|
||||
@@ -38,7 +38,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
|
||||
// Called on fresh installations and whenever the embedded outpost is deleted
|
||||
// automatically send the login URL when the user first visits the admin dashboard.
|
||||
async setOutpostHost(): Promise<void> {
|
||||
protected async setOutpostHost(): Promise<void> {
|
||||
const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList({
|
||||
managedIexact: "goauthentik.io/outposts/embedded",
|
||||
});
|
||||
@@ -53,7 +53,7 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
});
|
||||
}
|
||||
|
||||
getStatus(value: SystemInfo): Promise<AdminStatus> {
|
||||
protected getStatus(value: SystemInfo): Promise<AdminStatus> {
|
||||
if (!value.embeddedOutpostDisabled && value.embeddedOutpostHost === "") {
|
||||
this.statusSummary = msg("Warning");
|
||||
return Promise.resolve<AdminStatus>({
|
||||
@@ -84,11 +84,11 @@ export class SystemStatusCard extends AdminStatusCard<SystemInfo> {
|
||||
});
|
||||
}
|
||||
|
||||
renderHeader(): SlottedTemplateResult {
|
||||
protected override renderHeader(): SlottedTemplateResult {
|
||||
return msg("System status");
|
||||
}
|
||||
|
||||
renderValue(): SlottedTemplateResult {
|
||||
protected override renderValue(): SlottedTemplateResult {
|
||||
return this.statusSummary ? html`${this.statusSummary}` : nothing;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-version")
|
||||
export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
icon = "pf-icon pf-icon-bundle";
|
||||
public override icon = "pf-icon pf-icon-bundle";
|
||||
|
||||
getPrimaryValue(): Promise<Version> {
|
||||
protected getPrimaryValue(): Promise<Version> {
|
||||
return new AdminApi(DEFAULT_CONFIG).adminVersionRetrieve();
|
||||
}
|
||||
|
||||
getStatus(value: Version): Promise<AdminStatus> {
|
||||
protected getStatus(value: Version): Promise<AdminStatus> {
|
||||
if (value.buildHash) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-check-circle pf-m-success",
|
||||
@@ -48,11 +48,11 @@ export class VersionStatusCard extends AdminStatusCard<Version> {
|
||||
});
|
||||
}
|
||||
|
||||
renderHeader(): TemplateResult {
|
||||
protected override renderHeader(): TemplateResult {
|
||||
return html`${msg("Version")}`;
|
||||
}
|
||||
|
||||
renderValue(): TemplateResult {
|
||||
protected override renderValue(): TemplateResult {
|
||||
let text = this.value?.versionCurrent;
|
||||
const versionFamily = this.value?.versionCurrent.split(".");
|
||||
versionFamily?.pop();
|
||||
|
||||
@@ -10,17 +10,17 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-card-workers")
|
||||
export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
|
||||
icon = "pf-icon pf-icon-server";
|
||||
public override icon = "pf-icon pf-icon-server";
|
||||
|
||||
getPrimaryValue(): Promise<Worker[]> {
|
||||
protected getPrimaryValue(): Promise<Worker[]> {
|
||||
return new TasksApi(DEFAULT_CONFIG).tasksWorkersList();
|
||||
}
|
||||
|
||||
renderHeader(): TemplateResult {
|
||||
protected override renderHeader(): TemplateResult {
|
||||
return html`${msg("Workers")}`;
|
||||
}
|
||||
|
||||
getStatus(value: Worker[]): Promise<AdminStatus> {
|
||||
protected getStatus(value: Worker[]): Promise<AdminStatus> {
|
||||
if (value.length < 1) {
|
||||
return Promise.resolve<AdminStatus>({
|
||||
icon: "fa fa-times-circle pf-m-danger",
|
||||
@@ -37,7 +37,7 @@ export class WorkersStatusCard extends AdminStatusCard<Worker[]> {
|
||||
});
|
||||
}
|
||||
|
||||
renderValue() {
|
||||
protected override renderValue() {
|
||||
return html`${this.value?.length}`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-charts-admin-login-authorization")
|
||||
export class AdminLoginAuthorizeChart extends EventChart {
|
||||
async apiRequest(): Promise<EventVolume[]> {
|
||||
protected async apiRequest(): Promise<EventVolume[]> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
|
||||
actions: [
|
||||
EventActions.AuthorizeApplication,
|
||||
@@ -21,7 +21,7 @@ export class AdminLoginAuthorizeChart extends EventChart {
|
||||
});
|
||||
}
|
||||
|
||||
getChartData(data: EventVolume[]): ChartData {
|
||||
protected getChartData(data: EventVolume[]): ChartData {
|
||||
const optsMap = new Map<EventActions, Partial<ChartDataset>>();
|
||||
optsMap.set(EventActions.AuthorizeApplication, {
|
||||
label: msg("Authorizations"),
|
||||
|
||||
@@ -17,15 +17,15 @@ import { customElement, property } from "lit/decorators.js";
|
||||
@customElement("ak-charts-admin-model-per-day")
|
||||
export class AdminModelPerDay extends EventChart {
|
||||
@property()
|
||||
action: EventActions = EventActions.ModelCreated;
|
||||
public action: EventActions = EventActions.ModelCreated;
|
||||
|
||||
@property()
|
||||
label?: string;
|
||||
public label?: string;
|
||||
|
||||
@property({ attribute: false })
|
||||
query?: EventsEventsVolumeListRequest;
|
||||
public query?: EventsEventsVolumeListRequest;
|
||||
|
||||
async apiRequest(): Promise<EventVolume[]> {
|
||||
protected async apiRequest(): Promise<EventVolume[]> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
|
||||
action: this.action,
|
||||
historyDays: 30,
|
||||
@@ -33,7 +33,7 @@ export class AdminModelPerDay extends EventChart {
|
||||
});
|
||||
}
|
||||
|
||||
getChartData(data: EventVolume[]): ChartData {
|
||||
protected getChartData(data: EventVolume[]): ChartData {
|
||||
return this.eventVolume(data, {
|
||||
optsMap: new Map([
|
||||
[
|
||||
|
||||
@@ -16,11 +16,11 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-admin-status-chart-outpost")
|
||||
export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
getChartType(): string {
|
||||
public override getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
|
||||
getOptions(): ChartOptions {
|
||||
public override getOptions(): ChartOptions {
|
||||
return {
|
||||
plugins: {
|
||||
legend: {
|
||||
@@ -31,7 +31,7 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
};
|
||||
}
|
||||
|
||||
async apiRequest(): Promise<SummarizedSyncStatus[]> {
|
||||
protected async apiRequest(): Promise<SummarizedSyncStatus[]> {
|
||||
const api = new OutpostsApi(DEFAULT_CONFIG);
|
||||
const outposts = await api.outpostsInstancesList({});
|
||||
const outpostStats: SummarizedSyncStatus[] = [];
|
||||
@@ -65,7 +65,7 @@ export class OutpostStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
return outpostStats;
|
||||
}
|
||||
|
||||
getChartData(data: SummarizedSyncStatus[]): ChartData {
|
||||
protected getChartData(data: SummarizedSyncStatus[]): ChartData {
|
||||
return {
|
||||
labels: [msg("Healthy outposts"), msg("Outdated outposts"), msg("Unhealthy outposts")],
|
||||
datasets: data.map((d) => {
|
||||
|
||||
@@ -29,11 +29,11 @@ export interface SummarizedSyncStatus {
|
||||
|
||||
@customElement("ak-admin-status-chart-sync")
|
||||
export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
getChartType(): string {
|
||||
public override getChartType(): string {
|
||||
return "doughnut";
|
||||
}
|
||||
|
||||
getOptions(): ChartOptions {
|
||||
public override getOptions(): ChartOptions {
|
||||
return {
|
||||
plugins: {
|
||||
legend: {
|
||||
@@ -44,7 +44,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
};
|
||||
}
|
||||
|
||||
async fetchStatus<T>(
|
||||
protected async fetchStatus<T>(
|
||||
listObjects: () => Promise<PaginatedResponse<T>>,
|
||||
fetchSyncStatus: (element: T) => Promise<SyncStatus>,
|
||||
label: string,
|
||||
@@ -92,7 +92,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
};
|
||||
}
|
||||
|
||||
async apiRequest(): Promise<SummarizedSyncStatus[]> {
|
||||
protected async apiRequest(): Promise<SummarizedSyncStatus[]> {
|
||||
const statuses = [
|
||||
await this.fetchStatus(
|
||||
() => {
|
||||
@@ -158,7 +158,7 @@ export class SyncStatusChart extends AKChart<SummarizedSyncStatus[]> {
|
||||
return statuses;
|
||||
}
|
||||
|
||||
getChartData(data: SummarizedSyncStatus[]): ChartData {
|
||||
protected getChartData(data: SummarizedSyncStatus[]): ChartData {
|
||||
return {
|
||||
labels: [msg("Healthy"), msg("Failed"), msg("Unsynced / N/A")],
|
||||
datasets: data.map((d) => {
|
||||
|
||||
@@ -24,7 +24,7 @@ const hasLegalScheme = (url: string) =>
|
||||
|
||||
@customElement("ak-admin-settings-footer-link")
|
||||
export class FooterLinkInput extends AkControlElement<FooterLink> {
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
PFBase,
|
||||
PFInputGroup,
|
||||
PFFormControl,
|
||||
@@ -37,26 +37,26 @@ export class FooterLinkInput extends AkControlElement<FooterLink> {
|
||||
];
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
footerLink: FooterLink = {
|
||||
public footerLink: FooterLink = {
|
||||
name: "",
|
||||
href: "",
|
||||
};
|
||||
|
||||
@queryAll(".ak-form-control")
|
||||
controls?: HTMLInputElement[];
|
||||
protected controls?: HTMLInputElement[];
|
||||
|
||||
json() {
|
||||
public override json() {
|
||||
return Object.fromEntries(
|
||||
Array.from(this.controls ?? []).map((control) => [control.name, control.value]),
|
||||
) as unknown as FooterLink;
|
||||
}
|
||||
|
||||
get isValid() {
|
||||
public override get isValid() {
|
||||
const href = this.json()?.href ?? "";
|
||||
return hasLegalScheme(href) && URL.canParse(href);
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
const onChange = () => {
|
||||
this.dispatchEvent(new Event("change", { composed: true, bubbles: true }));
|
||||
};
|
||||
|
||||
@@ -33,19 +33,19 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
// Custom property accessors in Lit 2 require a manual call to requestUpdate(). See:
|
||||
// https://lit.dev/docs/v2/components/properties/#accessors-custom
|
||||
//
|
||||
set settings(value: Settings | undefined) {
|
||||
this._settings = value;
|
||||
public set settings(value: Settings | undefined) {
|
||||
this.#settings = value;
|
||||
this.requestUpdate();
|
||||
}
|
||||
|
||||
@property({ type: Object })
|
||||
get settings() {
|
||||
return this._settings;
|
||||
public get settings() {
|
||||
return this.#settings;
|
||||
}
|
||||
|
||||
private _settings?: Settings;
|
||||
#settings?: Settings;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
PFList,
|
||||
css`
|
||||
@@ -55,11 +55,11 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
`,
|
||||
];
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully updated settings.");
|
||||
}
|
||||
|
||||
async send(data: SettingsRequest): Promise<Settings> {
|
||||
protected async send(data: SettingsRequest): Promise<Settings> {
|
||||
const result = await new AdminApi(DEFAULT_CONFIG).adminSettingsUpdate({
|
||||
settingsRequest: data,
|
||||
});
|
||||
@@ -67,12 +67,12 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
return result;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`
|
||||
<ak-text-input
|
||||
name="avatars"
|
||||
label=${msg("Avatars")}
|
||||
value="${ifDefined(this._settings?.avatars)}"
|
||||
value="${ifDefined(this.#settings?.avatars)}"
|
||||
input-hint="code"
|
||||
.bighelp=${html`
|
||||
<p class="pf-c-form__helper-text">
|
||||
@@ -139,21 +139,21 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-switch-input
|
||||
name="defaultUserChangeName"
|
||||
label=${msg("Allow users to change name")}
|
||||
?checked="${this._settings?.defaultUserChangeName}"
|
||||
?checked="${this.#settings?.defaultUserChangeName}"
|
||||
help=${msg("Enable the ability for users to change their name.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="defaultUserChangeEmail"
|
||||
label=${msg("Allow users to change email")}
|
||||
?checked="${this._settings?.defaultUserChangeEmail}"
|
||||
?checked="${this.#settings?.defaultUserChangeEmail}"
|
||||
help=${msg("Enable the ability for users to change their email.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="defaultUserChangeUsername"
|
||||
label=${msg("Allow users to change username")}
|
||||
?checked="${this._settings?.defaultUserChangeUsername}"
|
||||
?checked="${this.#settings?.defaultUserChangeUsername}"
|
||||
help=${msg("Enable the ability for users to change their username.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
@@ -162,14 +162,14 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
label=${msg("Event retention")}
|
||||
input-hint="code"
|
||||
required
|
||||
value="${ifDefined(this._settings?.eventRetention)}"
|
||||
value="${ifDefined(this.#settings?.eventRetention)}"
|
||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||
${msg("Duration after which events will be deleted from the database.")}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
${msg(
|
||||
html`When using an external logging solution for archiving, this can be
|
||||
set to <code>minutes=5</code>.`,
|
||||
public set to <code>minutes=5</code>.`,
|
||||
)}
|
||||
</p>
|
||||
<p class="pf-c-form__helper-text">
|
||||
@@ -184,19 +184,19 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
label=${msg("Reputation: lower limit")}
|
||||
required
|
||||
name="reputationLowerLimit"
|
||||
value="${this._settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}"
|
||||
value="${this.#settings?.reputationLowerLimit ?? DEFAULT_REPUTATION_LOWER_LIMIT}"
|
||||
help=${msg("Reputation cannot decrease lower than this value. Zero or negative.")}
|
||||
></ak-number-input>
|
||||
<ak-number-input
|
||||
label=${msg("Reputation: upper limit")}
|
||||
required
|
||||
name="reputationUpperLimit"
|
||||
value="${this._settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}"
|
||||
value="${this.#settings?.reputationUpperLimit ?? DEFAULT_REPUTATION_UPPER_LIMIT}"
|
||||
help=${msg("Reputation cannot increase higher than this value. Zero or positive.")}
|
||||
></ak-number-input>
|
||||
<ak-form-element-horizontal label=${msg("Footer links")} name="footerLinks">
|
||||
<ak-array-input
|
||||
.items=${this._settings?.footerLinks ?? []}
|
||||
.items=${this.#settings?.footerLinks ?? []}
|
||||
.newItem=${() => ({ name: "", href: "" })}
|
||||
.row=${(f?: FooterLink) =>
|
||||
akFooterLinkInput({
|
||||
@@ -215,7 +215,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-switch-input
|
||||
name="gdprCompliance"
|
||||
label=${msg("GDPR compliance")}
|
||||
?checked="${this._settings?.gdprCompliance}"
|
||||
?checked="${this.#settings?.gdprCompliance}"
|
||||
help=${msg(
|
||||
"When enabled, all the events caused by a user will be deleted upon the user's deletion.",
|
||||
)}
|
||||
@@ -224,14 +224,14 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
<ak-switch-input
|
||||
name="impersonation"
|
||||
label=${msg("Impersonation")}
|
||||
?checked="${this._settings?.impersonation}"
|
||||
?checked="${this.#settings?.impersonation}"
|
||||
help=${msg("Globally enable/disable impersonation.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
<ak-switch-input
|
||||
name="impersonationRequireReason"
|
||||
label=${msg("Require reason for impersonation")}
|
||||
?checked="${this._settings?.impersonationRequireReason}"
|
||||
?checked="${this.#settings?.impersonationRequireReason}"
|
||||
help=${msg("Require administrators to provide a reason for impersonating a user.")}
|
||||
>
|
||||
</ak-switch-input>
|
||||
@@ -240,7 +240,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
label=${msg("Default token duration")}
|
||||
input-hint="code"
|
||||
required
|
||||
value="${ifDefined(this._settings?.defaultTokenDuration)}"
|
||||
value="${ifDefined(this.#settings?.defaultTokenDuration)}"
|
||||
.bighelp=${html`<p class="pf-c-form__helper-text">
|
||||
${msg("Default duration for generated tokens")}
|
||||
</p>
|
||||
@@ -251,7 +251,7 @@ export class AdminSettingsForm extends Form<SettingsRequest> {
|
||||
label=${msg("Default token length")}
|
||||
required
|
||||
name="defaultTokenLength"
|
||||
value="${this._settings?.defaultTokenLength ?? 60}"
|
||||
value="${this.#settings?.defaultTokenLength ?? 60}"
|
||||
help=${msg("Default length of generated tokens")}
|
||||
></ak-number-input>
|
||||
`;
|
||||
|
||||
@@ -33,7 +33,7 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-admin-settings")
|
||||
export class AdminSettingsPage extends AKElement {
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFPage,
|
||||
@@ -52,7 +52,7 @@ export class AdminSettingsPage extends AKElement {
|
||||
@state()
|
||||
protected settings?: Settings;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
|
||||
this.#refresh();
|
||||
@@ -74,7 +74,7 @@ export class AdminSettingsPage extends AKElement {
|
||||
return this.form?.reset();
|
||||
};
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!this.settings) return nothing;
|
||||
|
||||
return html`
|
||||
|
||||
@@ -12,16 +12,16 @@ import { customElement, property } from "lit/decorators.js";
|
||||
@customElement("ak-charts-application-authorize")
|
||||
export class ApplicationAuthorizeChart extends EventChart {
|
||||
@property({ attribute: "application-id" })
|
||||
applicationId!: string;
|
||||
public applicationId!: string;
|
||||
|
||||
async apiRequest(): Promise<EventVolume[]> {
|
||||
protected async apiRequest(): Promise<EventVolume[]> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
|
||||
action: EventActions.AuthorizeApplication,
|
||||
contextAuthorizedApp: this.applicationId.replaceAll("-", ""),
|
||||
});
|
||||
}
|
||||
|
||||
getChartData(data: EventVolume[]): ChartData {
|
||||
protected getChartData(data: EventVolume[]): ChartData {
|
||||
return this.eventVolume(data, {
|
||||
optsMap: new Map([
|
||||
[
|
||||
|
||||
@@ -24,19 +24,19 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
|
||||
@customElement("ak-application-check-access-form")
|
||||
export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
@property({ attribute: false })
|
||||
application!: Application;
|
||||
public application!: Application;
|
||||
|
||||
@property({ attribute: false })
|
||||
result: PolicyTestResult | null = null;
|
||||
public result: PolicyTestResult | null = null;
|
||||
|
||||
@property({ attribute: false })
|
||||
request?: number;
|
||||
public request?: number;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully sent test-request.");
|
||||
}
|
||||
|
||||
async send(data: { forUser: number }): Promise<PolicyTestResult> {
|
||||
protected async send(data: { forUser: number }): Promise<PolicyTestResult> {
|
||||
this.request = data.forUser;
|
||||
const result = await new CoreApi(DEFAULT_CONFIG).coreApplicationsCheckAccessRetrieve({
|
||||
slug: this.application?.slug,
|
||||
@@ -45,14 +45,14 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
return (this.result = result);
|
||||
}
|
||||
|
||||
reset(): void {
|
||||
public override reset(): void {
|
||||
super.reset();
|
||||
this.result = null;
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
protected renderResult(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Passing")}>
|
||||
<div class="pf-c-form__group-label">
|
||||
@@ -92,7 +92,7 @@ export class ApplicationCheckAccessForm extends Form<{ forUser: number }> {
|
||||
`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("User")} required name="forUser">
|
||||
<ak-search-select
|
||||
.fetchObjects=${async (query?: string): Promise<User[]> => {
|
||||
|
||||
@@ -33,14 +33,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-application-form")
|
||||
export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Application, string>) {
|
||||
constructor() {
|
||||
super();
|
||||
this.handleConfirmBackchannelProviders = this.handleConfirmBackchannelProviders.bind(this);
|
||||
this.makeRemoveBackchannelProviderHandler =
|
||||
this.makeRemoveBackchannelProviderHandler.bind(this);
|
||||
}
|
||||
|
||||
async loadInstance(pk: string): Promise<Application> {
|
||||
protected async loadInstance(pk: string): Promise<Application> {
|
||||
const app = await new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({
|
||||
slug: pk,
|
||||
});
|
||||
@@ -50,21 +43,21 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
provider?: number;
|
||||
public provider?: number;
|
||||
|
||||
@state()
|
||||
backchannelProviders: Provider[] = [];
|
||||
protected backchannelProviders: Provider[] = [];
|
||||
|
||||
@property({ type: Boolean })
|
||||
clearIcon = false;
|
||||
public clearIcon = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated application.")
|
||||
: msg("Successfully created application.");
|
||||
}
|
||||
|
||||
async send(data: Application): Promise<Application | void> {
|
||||
protected async send(data: Application): Promise<Application | void> {
|
||||
let app: Application;
|
||||
data.backchannelProviders = this.backchannelProviders.map((p) => p.pk);
|
||||
if (this.instance) {
|
||||
@@ -97,21 +90,21 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
return app;
|
||||
}
|
||||
|
||||
handleConfirmBackchannelProviders(items: Provider[]) {
|
||||
#confirmBackchannelProviders = (items: Provider[]) => {
|
||||
this.backchannelProviders = items;
|
||||
this.requestUpdate();
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
makeRemoveBackchannelProviderHandler(provider: Provider) {
|
||||
#removeBackchannelProvider = (provider: Provider) => {
|
||||
return () => {
|
||||
const idx = this.backchannelProviders.indexOf(provider);
|
||||
this.backchannelProviders.splice(idx, 1);
|
||||
this.requestUpdate();
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
handleClearIcon(ev: Event) {
|
||||
protected handleClearIcon(ev: Event) {
|
||||
ev.stopPropagation();
|
||||
if (!(ev instanceof InputEvent) || !ev.target) {
|
||||
return;
|
||||
@@ -119,7 +112,7 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
this.clearIcon = !!(ev.target as HTMLInputElement).checked;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
const alertMsg = msg(
|
||||
"Using this form will only create an Application. In order to authenticate with the application, you will have to manually pair it with a Provider.",
|
||||
);
|
||||
@@ -164,8 +157,8 @@ export class ApplicationForm extends WithCapabilitiesConfig(ModelForm<Applicatio
|
||||
"Select backchannel providers which augment the functionality of the main provider.",
|
||||
)}
|
||||
.providers=${this.backchannelProviders}
|
||||
.confirm=${this.handleConfirmBackchannelProviders}
|
||||
.remover=${this.makeRemoveBackchannelProviderHandler}
|
||||
.confirm=${this.#confirmBackchannelProviders}
|
||||
.remover=${this.#removeBackchannelProvider}
|
||||
.tooltip=${html`<pf-tooltip
|
||||
position="top"
|
||||
content=${msg("Add provider")}
|
||||
|
||||
@@ -43,37 +43,40 @@ export const applicationListStyle = css`
|
||||
|
||||
@customElement("ak-application-list")
|
||||
export class ApplicationListPage extends WithBrandConfig(TablePage<Application>) {
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Applications");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg(
|
||||
str`External applications that use ${this.brandingTitle} as an identity provider via protocols like OAuth2 and SAML. All applications are shown here, even ones you cannot access.`,
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-applications";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Application>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Application>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
superuserFullList: true,
|
||||
});
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...TablePage.styles, PFCard, applicationListStyle];
|
||||
public static styles: CSSResult[] = [...TablePage.styles, PFCard, applicationListStyle];
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(""),
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
@@ -84,7 +87,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
];
|
||||
}
|
||||
|
||||
protected renderSidebarAfter(): TemplateResult {
|
||||
protected override renderSidebarAfter(): TemplateResult {
|
||||
return html`<div class="pf-c-sidebar__panel pf-m-width-25">
|
||||
<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">
|
||||
@@ -94,7 +97,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Application(s)")}
|
||||
@@ -116,7 +119,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Application): TemplateResult[] {
|
||||
protected row(item: Application): TemplateResult[] {
|
||||
return [
|
||||
html`<ak-app-icon
|
||||
name=${item.name}
|
||||
@@ -154,7 +157,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html` <ak-application-wizard .open=${getURLParam("createWizard", false)}>
|
||||
<button
|
||||
slot="trigger"
|
||||
@@ -172,7 +175,7 @@ export class ApplicationListPage extends WithBrandConfig(TablePage<Application>)
|
||||
</ak-forms-modal>`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html` ${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
successMessage=${msg("Successfully cleared application cache")}
|
||||
|
||||
@@ -41,15 +41,15 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-application-view")
|
||||
export class ApplicationViewPage extends AKElement {
|
||||
@property({ type: String })
|
||||
applicationSlug?: string;
|
||||
public applicationSlug?: string;
|
||||
|
||||
@state()
|
||||
application?: Application;
|
||||
protected application?: Application;
|
||||
|
||||
@state()
|
||||
missingOutpost = false;
|
||||
protected missingOutpost = false;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFList,
|
||||
PFBanner,
|
||||
@@ -61,7 +61,7 @@ export class ApplicationViewPage extends AKElement {
|
||||
PFCard,
|
||||
];
|
||||
|
||||
fetchIsMissingOutpost(providersByPk: Array<number>) {
|
||||
protected fetchIsMissingOutpost(providersByPk: Array<number>) {
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesList({
|
||||
providersByPk,
|
||||
@@ -74,7 +74,7 @@ export class ApplicationViewPage extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
fetchApplication(slug: string) {
|
||||
protected fetchApplication(slug: string) {
|
||||
new CoreApi(DEFAULT_CONFIG).coreApplicationsRetrieve({ slug }).then((app) => {
|
||||
this.application = app;
|
||||
if (
|
||||
@@ -89,13 +89,13 @@ export class ApplicationViewPage extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
willUpdate(changedProperties: PropertyValues<this>) {
|
||||
public override willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("applicationSlug") && this.applicationSlug) {
|
||||
this.fetchApplication(this.applicationSlug);
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
header=${this.application?.name || msg("Loading")}
|
||||
description=${ifDefined(this.application?.metaPublisher)}
|
||||
@@ -110,7 +110,7 @@ export class ApplicationViewPage extends AKElement {
|
||||
${this.renderApp()}`;
|
||||
}
|
||||
|
||||
renderApp(): TemplateResult {
|
||||
protected renderApp(): TemplateResult {
|
||||
if (!this.application) {
|
||||
return html`<ak-empty-state default-label></ak-empty-state>`;
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ const closeButtonIcon = html`<svg
|
||||
|
||||
@customElement("ak-application-wizard-hint")
|
||||
export class AkApplicationWizardHint extends AKElement implements ShowHintControllerHost {
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
PFBase,
|
||||
PFButton,
|
||||
PFPage,
|
||||
@@ -51,14 +51,14 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
|
||||
];
|
||||
|
||||
@property({ type: Boolean, attribute: "show-hint" })
|
||||
forceHint: boolean = false;
|
||||
public forceHint: boolean = false;
|
||||
|
||||
@state()
|
||||
showHint: boolean = true;
|
||||
public showHint: boolean = true;
|
||||
|
||||
showHintController: ShowHintController;
|
||||
public showHintController: ShowHintController;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.showHintController = new ShowHintController(
|
||||
this,
|
||||
@@ -66,7 +66,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
|
||||
);
|
||||
}
|
||||
|
||||
renderReminder() {
|
||||
protected renderReminder() {
|
||||
const sectionStyles = {
|
||||
paddingBottom: "0",
|
||||
marginBottom: "-0.5rem",
|
||||
@@ -98,7 +98,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
|
||||
</section>`;
|
||||
}
|
||||
|
||||
renderHint() {
|
||||
protected renderHint() {
|
||||
return html` <section class="pf-c-page__main-section pf-m-no-padding-mobile">
|
||||
<ak-hint>
|
||||
<ak-hint-body>
|
||||
@@ -122,7 +122,7 @@ export class AkApplicationWizardHint extends AKElement implements ShowHintContro
|
||||
</section>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return this.showHint || this.forceHint ? this.renderHint() : this.renderReminder();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,33 +13,33 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-provider-select-table")
|
||||
export class ProviderSelectModal extends TableModal<Provider> {
|
||||
checkbox = true;
|
||||
checkboxChip = true;
|
||||
public override checkbox = true;
|
||||
public override checkboxChip = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
backchannel = false;
|
||||
public backchannel = false;
|
||||
|
||||
@property()
|
||||
confirm!: (selectedItems: Provider[]) => Promise<unknown>;
|
||||
public confirm!: (selectedItems: Provider[]) => Promise<unknown>;
|
||||
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Provider>> {
|
||||
return new ProvidersApi(DEFAULT_CONFIG).providersAllList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
backchannel: this.backchannel,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Name"), "username"), new TableColumn(msg("Type"))];
|
||||
}
|
||||
|
||||
row(item: Provider): TemplateResult[] {
|
||||
protected row(item: Provider): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.name}</div>
|
||||
@@ -48,11 +48,11 @@ export class ProviderSelectModal extends TableModal<Provider> {
|
||||
];
|
||||
}
|
||||
|
||||
renderSelectedChip(item: Provider): TemplateResult {
|
||||
protected override renderSelectedChip(item: Provider): TemplateResult {
|
||||
return html`${item.name}`;
|
||||
}
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
protected override renderModalInner(): TemplateResult {
|
||||
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1 class="pf-c-title pf-m-2xl">
|
||||
|
||||
@@ -24,38 +24,38 @@ export class AkBackchannelProvidersInput extends AKElement {
|
||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
||||
// general.
|
||||
|
||||
protected createRenderRoot() {
|
||||
protected override createRenderRoot() {
|
||||
return this as HTMLElement;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
name!: string;
|
||||
public name!: string;
|
||||
|
||||
@property({ type: String })
|
||||
label = "";
|
||||
public label = "";
|
||||
|
||||
@property({ type: Array })
|
||||
providers: Provider[] = [];
|
||||
public providers: Provider[] = [];
|
||||
|
||||
@property({ type: Object })
|
||||
tooltip?: TemplateResult;
|
||||
public tooltip?: TemplateResult;
|
||||
|
||||
@property({ attribute: false, type: Object })
|
||||
confirm!: ({ items }: { items: Provider[] }) => Promise<void>;
|
||||
public confirm!: ({ items }: { items: Provider[] }) => Promise<void>;
|
||||
|
||||
@property({ attribute: false, type: Object })
|
||||
remover!: (provider: Provider) => () => void;
|
||||
public remover!: (provider: Provider) => () => void;
|
||||
|
||||
@property({ type: String })
|
||||
value = "";
|
||||
public value = "";
|
||||
|
||||
@property({ type: Boolean })
|
||||
required = false;
|
||||
public required = false;
|
||||
|
||||
@property({ type: String })
|
||||
help = "";
|
||||
public help = "";
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
const renderOneChip = (provider: Provider) =>
|
||||
html`<ak-chip
|
||||
removable
|
||||
|
||||
@@ -34,38 +34,38 @@ export class AkProviderInput extends AKElement {
|
||||
// TODO: This abstraction is wrong; it's putting *more* layers in as a way of managing the
|
||||
// visual clutter and legibility issues of ak-form-elemental-horizontal and patternfly in
|
||||
// general.
|
||||
protected createRenderRoot() {
|
||||
protected override createRenderRoot() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@property({ type: String })
|
||||
name!: string;
|
||||
public name!: string;
|
||||
|
||||
@property({ type: String })
|
||||
label = "";
|
||||
public label = "";
|
||||
|
||||
@property({ type: Number })
|
||||
value?: number;
|
||||
public value?: number;
|
||||
|
||||
@property({ type: Boolean })
|
||||
required = false;
|
||||
public required = false;
|
||||
|
||||
@property({ type: Boolean })
|
||||
blankable = false;
|
||||
public blankable = false;
|
||||
|
||||
@property({ type: String })
|
||||
help = "";
|
||||
public help = "";
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
}
|
||||
|
||||
selected(item: Provider) {
|
||||
protected selected(item: Provider) {
|
||||
return this.value !== undefined && this.value === item.pk;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html` <ak-form-element-horizontal label=${this.label} name=${this.name}>
|
||||
<ak-search-select
|
||||
.selected=${this.selected}
|
||||
|
||||
@@ -20,25 +20,25 @@ import PFContent from "@patternfly/patternfly/components/Content/content.css";
|
||||
|
||||
@customElement("ak-application-entitlement-form")
|
||||
export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement, string> {
|
||||
async loadInstance(pk: string): Promise<ApplicationEntitlement> {
|
||||
protected async loadInstance(pk: string): Promise<ApplicationEntitlement> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsRetrieve({
|
||||
pbmUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
@property()
|
||||
targetPk?: string;
|
||||
public targetPk?: string;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
if (this.instance?.pbmUuid) {
|
||||
return msg("Successfully updated entitlement.");
|
||||
}
|
||||
return msg("Successfully created entitlement.");
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFContent];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFContent];
|
||||
|
||||
send(data: ApplicationEntitlement): Promise<unknown> {
|
||||
protected send(data: ApplicationEntitlement): Promise<unknown> {
|
||||
if (this.targetPk) {
|
||||
data.app = this.targetPk;
|
||||
}
|
||||
@@ -53,7 +53,7 @@ export class ApplicationEntitlementForm extends ModelForm<ApplicationEntitlement
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -28,26 +28,26 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@customElement("ak-application-entitlements-list")
|
||||
export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
@property()
|
||||
app?: string;
|
||||
public app?: string;
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
|
||||
order = "order";
|
||||
public override order = "order";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<ApplicationEntitlement>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<ApplicationEntitlement>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreApplicationEntitlementsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
app: this.app || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [new TableColumn(msg("Name"), "name"), new TableColumn(msg("Actions"))];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Application entitlement(s)")}
|
||||
@@ -69,7 +69,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: ApplicationEntitlement): TemplateResult[] {
|
||||
protected row(item: ApplicationEntitlement): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ak-forms-modal size=${PFSize.Medium}>
|
||||
@@ -95,7 +95,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: ApplicationEntitlement): TemplateResult {
|
||||
protected override renderExpanded(item: ApplicationEntitlement): TemplateResult {
|
||||
return html`<td></td>
|
||||
<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -118,7 +118,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
protected override renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state icon="pf-icon-module"
|
||||
><span>${msg("No app entitlements created.")}</span>
|
||||
@@ -133,7 +133,7 @@ export class ApplicationEntitlementsPage extends Table<ApplicationEntitlement> {
|
||||
);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`<ak-forms-modal size=${PFSize.Medium}>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
<span slot="header"> ${msg("Create Entitlement")} </span>
|
||||
|
||||
@@ -21,22 +21,24 @@ import { msg } from "@lit/localize";
|
||||
import { property, query } from "lit/decorators.js";
|
||||
|
||||
export class ApplicationWizardStep<T = Record<string, unknown>> extends WizardStep {
|
||||
static styles = [...WizardStep.styles, ...styles];
|
||||
public static override styles = [...WizardStep.styles, ...styles];
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
wizard!: ApplicationWizardState;
|
||||
public wizard!: ApplicationWizardState;
|
||||
|
||||
// As recommended in [WizardStep](../../../components/ak-wizard/WizardStep.ts), we override
|
||||
// these fields and provide them to all the child classes.
|
||||
wizardTitle = msg("New application");
|
||||
wizardDescription = msg("Create a new application and configure a provider for it.");
|
||||
canCancel = true;
|
||||
public override wizardTitle = msg("New application");
|
||||
public override wizardDescription = msg(
|
||||
"Create a new application and configure a provider for it.",
|
||||
);
|
||||
public override canCancel = true;
|
||||
|
||||
// This should be overridden in the children for more precise targeting.
|
||||
@query("form")
|
||||
form!: HTMLFormElement;
|
||||
protected form!: HTMLFormElement;
|
||||
|
||||
get formValues(): T {
|
||||
public get formValues(): T {
|
||||
return serializeForm<T>([
|
||||
...this.form.querySelectorAll("ak-form-element-horizontal"),
|
||||
...this.form.querySelectorAll("[data-ak-control]"),
|
||||
|
||||
@@ -35,19 +35,19 @@ const freshWizardState = (): ApplicationWizardState => ({
|
||||
@customElement("ak-application-wizard-main")
|
||||
export class AkApplicationWizardMain extends AKElement {
|
||||
@state()
|
||||
wizard: ApplicationWizardState = freshWizardState();
|
||||
protected wizard: ApplicationWizardState = freshWizardState();
|
||||
|
||||
wizardProviderProvider = new ContextProvider(this, {
|
||||
protected wizardProviderProvider = new ContextProvider(this, {
|
||||
context: applicationWizardProvidersContext,
|
||||
initialValue: [],
|
||||
});
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.addEventListener(WizardUpdateEvent.eventName, this.handleUpdate);
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
new ProvidersApi(DEFAULT_CONFIG).providersAllTypesList().then((providerTypes) => {
|
||||
const wizardReadyProviders = Object.keys(providerTypeRenderers);
|
||||
@@ -70,7 +70,7 @@ export class AkApplicationWizardMain extends AKElement {
|
||||
|
||||
// This is the actual top of the Wizard; so this is where we accept the update information and
|
||||
// incorporate it into the wizard.
|
||||
handleUpdate(ev: WizardUpdateEvent<ApplicationWizardStateUpdate>) {
|
||||
protected handleUpdate(ev: WizardUpdateEvent<ApplicationWizardStateUpdate>) {
|
||||
ev.stopPropagation();
|
||||
const update = ev.content;
|
||||
if (update !== undefined) {
|
||||
@@ -81,7 +81,7 @@ export class AkApplicationWizardMain extends AKElement {
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`<ak-wizard-steps>
|
||||
<ak-application-wizard-application-step
|
||||
slot="application"
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import "./ak-application-wizard-main.js";
|
||||
|
||||
import { ModalButton } from "#elements/buttons/ModalButton";
|
||||
import { bound } from "#elements/decorators/bound";
|
||||
|
||||
import { WizardCloseEvent } from "#components/ak-wizard/events";
|
||||
|
||||
@@ -10,18 +9,17 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-application-wizard")
|
||||
export class AkApplicationWizard extends ModalButton {
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.addEventListener(WizardCloseEvent.eventName, this.onCloseEvent);
|
||||
this.addEventListener(WizardCloseEvent.eventName, this.#closeListener);
|
||||
}
|
||||
|
||||
@bound
|
||||
onCloseEvent(ev: WizardCloseEvent) {
|
||||
ev.stopPropagation();
|
||||
#closeListener = (event: WizardCloseEvent) => {
|
||||
event.stopPropagation();
|
||||
this.open = false;
|
||||
}
|
||||
};
|
||||
|
||||
renderModalInner() {
|
||||
protected override renderModalInner() {
|
||||
return html` <ak-application-wizard-main> </ak-application-wizard-main>`;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import PFTitle from "@patternfly/patternfly/components/Title/title.css";
|
||||
|
||||
@customElement("ak-wizard-title")
|
||||
export class AkWizardTitle extends AKElement {
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
PFContent,
|
||||
PFTitle,
|
||||
css`
|
||||
@@ -18,7 +18,7 @@ export class AkWizardTitle extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`<div class="ak-bottom-spacing pf-c-content">
|
||||
<h3><slot></slot></h3>
|
||||
</div>`;
|
||||
|
||||
@@ -34,21 +34,21 @@ const isStr = (v: any): v is string => typeof v === "string";
|
||||
|
||||
@customElement("ak-application-wizard-application-step")
|
||||
export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
label = msg("Application");
|
||||
public override label = msg("Application");
|
||||
|
||||
@state()
|
||||
errors = new Map<string, string>();
|
||||
protected errors = new Map<string, string>();
|
||||
|
||||
@query("form#applicationform")
|
||||
form!: HTMLFormElement;
|
||||
protected override form!: HTMLFormElement;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
// This is the first step. Ensure it is always enabled.
|
||||
this.enabled = true;
|
||||
}
|
||||
|
||||
errorMessages(name: string) {
|
||||
protected errorMessages(name: string) {
|
||||
return this.errors.has(name)
|
||||
? [this.errors.get(name)]
|
||||
: (this.wizard.errors?.app?.[name] ??
|
||||
@@ -56,11 +56,11 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
[]);
|
||||
}
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
return [{ kind: "next", destination: "provider-choice" }, { kind: "cancel" }];
|
||||
}
|
||||
|
||||
get valid() {
|
||||
public get valid() {
|
||||
this.errors = new Map();
|
||||
const values = trimMany(this.formValues ?? {}, ["metaLaunchUrl", "name", "slug"]);
|
||||
|
||||
@@ -81,7 +81,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
return this.errors.size === 0;
|
||||
}
|
||||
|
||||
override handleButton(button: NavigableButton) {
|
||||
protected override handleButton(button: NavigableButton) {
|
||||
if (button.kind === "next") {
|
||||
if (!this.valid) {
|
||||
this.handleEnabling({
|
||||
@@ -109,7 +109,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
super.handleButton(button);
|
||||
}
|
||||
|
||||
renderForm(app: Partial<ApplicationRequest>, errors: ValidationRecord) {
|
||||
protected renderForm(app: Partial<ApplicationRequest>, errors: ValidationRecord) {
|
||||
return html` <ak-wizard-title>${msg("Configure The Application")}</ak-wizard-title>
|
||||
<form id="applicationform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
<ak-text-input
|
||||
@@ -177,7 +177,7 @@ export class ApplicationWizardApplicationStep extends ApplicationWizardStep {
|
||||
</form>`;
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
if (!(this.wizard.app && this.wizard.errors)) {
|
||||
throw new Error("Application Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -35,9 +35,9 @@ const COLUMNS = [
|
||||
|
||||
@customElement("ak-application-wizard-bindings-step")
|
||||
export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
label = msg("Configure Bindings");
|
||||
public override label = msg("Configure Bindings");
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
return [
|
||||
{ kind: "next", destination: "submit" },
|
||||
{ kind: "back", destination: "provider" },
|
||||
@@ -46,9 +46,9 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
}
|
||||
|
||||
@query("ak-select-table")
|
||||
selectTable!: SelectTable;
|
||||
protected selectTable!: SelectTable;
|
||||
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
...super.styles,
|
||||
PFCard,
|
||||
css`
|
||||
@@ -58,7 +58,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
`,
|
||||
];
|
||||
|
||||
get bindingsAsColumns() {
|
||||
public get bindingsAsColumns() {
|
||||
return this.wizard.bindings.map((binding, index) => {
|
||||
const { order, enabled, timeout } = binding;
|
||||
const isSet = P.union(P.string.minLength(1), P.number);
|
||||
@@ -85,13 +85,13 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
|
||||
// TODO Fix those dispatches so that we handle them here, in this component, and *choose* how to
|
||||
// forward them.
|
||||
onBindingEvent(binding?: number) {
|
||||
protected onBindingEvent(binding?: number) {
|
||||
this.handleUpdate({ currentBinding: binding ?? -1 }, "edit-binding", {
|
||||
enable: "edit-binding",
|
||||
});
|
||||
}
|
||||
|
||||
onDeleteBindings() {
|
||||
protected onDeleteBindings() {
|
||||
const toDelete = this.selectTable
|
||||
.json()
|
||||
.map((i) => (typeof i === "string" ? parseInt(i, 10) : i));
|
||||
@@ -99,7 +99,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
this.handleUpdate({ bindings }, "bindings");
|
||||
}
|
||||
|
||||
renderEmptyCollection() {
|
||||
protected renderEmptyCollection() {
|
||||
return html`<ak-wizard-title
|
||||
>${msg("Configure Policy/User/Group Bindings")}</ak-wizard-title
|
||||
>
|
||||
@@ -133,7 +133,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderCollection() {
|
||||
protected 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.")}
|
||||
@@ -152,7 +152,7 @@ export class ApplicationWizardBindingsStep extends ApplicationWizardStep {
|
||||
></ak-select-table>`;
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
if ((this.wizard.bindings ?? []).length === 0) {
|
||||
return this.renderEmptyCollection();
|
||||
}
|
||||
|
||||
@@ -44,24 +44,27 @@ const PASS_FAIL = [
|
||||
|
||||
@customElement("ak-application-wizard-edit-binding-step")
|
||||
export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
label = msg("Edit Binding");
|
||||
public override label = msg("Edit Binding");
|
||||
|
||||
hide = true;
|
||||
public override hide = true;
|
||||
|
||||
@query("form#bindingform")
|
||||
form!: HTMLFormElement;
|
||||
protected override form!: HTMLFormElement;
|
||||
|
||||
@query(".policy-search-select")
|
||||
searchSelect!: SearchSelectBase<Policy> | SearchSelectBase<Group> | SearchSelectBase<User>;
|
||||
protected searchSelect!:
|
||||
| SearchSelectBase<Policy>
|
||||
| SearchSelectBase<Group>
|
||||
| SearchSelectBase<User>;
|
||||
|
||||
@state()
|
||||
policyGroupUser: target = target.policy;
|
||||
protected policyGroupUser: target = target.policy;
|
||||
|
||||
instanceId = -1;
|
||||
protected instanceId = -1;
|
||||
|
||||
instance?: PolicyBinding;
|
||||
protected instance?: PolicyBinding;
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
return [
|
||||
{ kind: "next", label: msg("Save Binding"), destination: "bindings" },
|
||||
{ kind: "back", destination: "bindings" },
|
||||
@@ -69,7 +72,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
];
|
||||
}
|
||||
|
||||
override handleButton(button: NavigableButton) {
|
||||
protected override handleButton(button: NavigableButton) {
|
||||
if (button.kind === "next") {
|
||||
if (!this.form.checkValidity()) {
|
||||
return;
|
||||
@@ -98,7 +101,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
|
||||
// The search select configurations for the three different types of fetches that we care about,
|
||||
// policy, user, and group, all using the SearchSelectEZ protocol.
|
||||
searchSelectConfigs(kind: target) {
|
||||
protected searchSelectConfigs(kind: target) {
|
||||
switch (kind) {
|
||||
case target.policy:
|
||||
return {
|
||||
@@ -151,7 +154,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
}
|
||||
}
|
||||
|
||||
renderSearch(title: string, policyKind: target) {
|
||||
protected renderSearch(title: string, policyKind: target) {
|
||||
if (policyKind !== this.policyGroupUser) {
|
||||
return nothing;
|
||||
}
|
||||
@@ -165,7 +168,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(instance?: PolicyBinding) {
|
||||
protected renderForm(instance?: PolicyBinding) {
|
||||
return html`<ak-wizard-title>${msg("Create a Policy/User/Group Binding")}</ak-wizard-title>
|
||||
<form id="bindingform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
<div class="pf-c-card pf-m-selectable pf-m-selected">
|
||||
@@ -218,7 +221,7 @@ export class ApplicationWizardEditBindingStep extends ApplicationWizardStep {
|
||||
</form>`;
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
if (!(this.wizard.bindings && this.wizard.errors)) {
|
||||
throw new Error("Application Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ import "#elements/wizard/TypeCreateWizardPage";
|
||||
import { applicationWizardProvidersContext } from "../ContextIdentity.js";
|
||||
import { type LocalTypeCreate } from "./ProviderChoices.js";
|
||||
|
||||
import { bound } from "#elements/decorators/bound";
|
||||
import { WithLicenseSummary } from "#elements/mixins/license";
|
||||
import { TypeCreateWizardPageLayouts } from "#elements/wizard/TypeCreateWizardPage";
|
||||
|
||||
@@ -15,8 +14,6 @@ import type { NavigableButton, WizardButton } from "#components/ak-wizard/types"
|
||||
|
||||
import { ApplicationWizardStep } from "#admin/applications/wizard/ApplicationWizardStep";
|
||||
|
||||
import { TypeCreate } from "@goauthentik/api";
|
||||
|
||||
import { consume } from "@lit/context";
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
@@ -24,15 +21,15 @@ import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-application-wizard-provider-choice-step")
|
||||
export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(ApplicationWizardStep) {
|
||||
label = msg("Choose a Provider");
|
||||
public override label = msg("Choose a Provider");
|
||||
|
||||
@state()
|
||||
failureMessage = "";
|
||||
protected failureMessage = "";
|
||||
|
||||
@consume({ context: applicationWizardProvidersContext, subscribe: true })
|
||||
public providerModelsList!: LocalTypeCreate[];
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
return [
|
||||
{ kind: "next", destination: "provider" },
|
||||
{ kind: "back", destination: "application" },
|
||||
@@ -40,7 +37,7 @@ export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(Appl
|
||||
];
|
||||
}
|
||||
|
||||
override handleButton(button: NavigableButton) {
|
||||
protected override handleButton(button: NavigableButton) {
|
||||
this.failureMessage = "";
|
||||
if (button.kind === "next") {
|
||||
if (!this.wizard.providerModel) {
|
||||
@@ -54,14 +51,7 @@ export class ApplicationWizardProviderChoiceStep extends WithLicenseSummary(Appl
|
||||
super.handleButton(button);
|
||||
}
|
||||
|
||||
@bound
|
||||
onSelect(ev: CustomEvent<LocalTypeCreate>) {
|
||||
ev.stopPropagation();
|
||||
const detail: TypeCreate = ev.detail;
|
||||
this.handleUpdate({ providerModel: detail.modelName });
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
const selectedTypes = this.providerModelsList.filter(
|
||||
(t) => t.modelName === this.wizard.providerModel,
|
||||
);
|
||||
|
||||
@@ -30,20 +30,20 @@ const providerToTag = new Map([
|
||||
@customElement("ak-application-wizard-provider-step")
|
||||
export class ApplicationWizardProviderStep extends ApplicationWizardStep {
|
||||
@state()
|
||||
label = msg("Configure Provider");
|
||||
public override label = msg("Configure Provider");
|
||||
|
||||
@query("#providerform")
|
||||
element!: ApplicationWizardProviderForm<OneOfProvider>;
|
||||
protected element!: ApplicationWizardProviderForm<OneOfProvider>;
|
||||
|
||||
get valid() {
|
||||
public get valid() {
|
||||
return this.element.valid;
|
||||
}
|
||||
|
||||
get formValues() {
|
||||
public override get formValues() {
|
||||
return this.element.formValues;
|
||||
}
|
||||
|
||||
override handleButton(button: NavigableButton) {
|
||||
protected override handleButton(button: NavigableButton) {
|
||||
if (button.kind === "next") {
|
||||
if (!this.valid) {
|
||||
this.handleEnabling({
|
||||
@@ -66,7 +66,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
|
||||
super.handleButton(button);
|
||||
}
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
return [
|
||||
{ kind: "next", destination: "bindings" },
|
||||
{ kind: "back", destination: "provider-choice" },
|
||||
@@ -74,7 +74,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
|
||||
];
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
if (!this.wizard.providerModel) {
|
||||
throw new Error("Attempted to access provider page without providing a provider type.");
|
||||
}
|
||||
@@ -97,7 +97,7 @@ export class ApplicationWizardProviderStep extends ApplicationWizardStep {
|
||||
: nothing;
|
||||
}
|
||||
|
||||
updated(changed: PropertyValues<this>) {
|
||||
public override updated(changed: PropertyValues<this>) {
|
||||
if (changed.has("wizard")) {
|
||||
const label = this.element?.label ?? this.label;
|
||||
if (label !== this.label) {
|
||||
|
||||
@@ -80,7 +80,7 @@ const cleanBinding = (binding: PolicyBinding): TransactionPolicyBindingRequest =
|
||||
|
||||
@customElement("ak-application-wizard-submit-step")
|
||||
export class ApplicationWizardSubmitStep extends CustomEmitterElement(ApplicationWizardStep) {
|
||||
static styles = [
|
||||
public static styles = [
|
||||
...ApplicationWizardStep.styles,
|
||||
PFBullseye,
|
||||
PFEmptyState,
|
||||
@@ -95,12 +95,12 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
`,
|
||||
];
|
||||
|
||||
label = msg("Review and Submit Application");
|
||||
public override label = msg("Review and Submit Application");
|
||||
|
||||
@state()
|
||||
state: SubmitStates = "reviewing";
|
||||
protected state: SubmitStates = "reviewing";
|
||||
|
||||
async send() {
|
||||
protected async send() {
|
||||
const app = this.wizard.app;
|
||||
const provider = this.wizard.provider as ModelRequest;
|
||||
|
||||
@@ -177,7 +177,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
});
|
||||
}
|
||||
|
||||
override handleButton(button: WizardButton) {
|
||||
protected override handleButton(button: WizardButton) {
|
||||
match([button.kind, this.state])
|
||||
.with([P.union("back", "cancel"), P._], () => {
|
||||
super.handleButton(button);
|
||||
@@ -198,7 +198,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
});
|
||||
}
|
||||
|
||||
get buttons(): WizardButton[] {
|
||||
public override get buttons(): WizardButton[] {
|
||||
const forReview: WizardButton[] = [
|
||||
{ kind: "next", label: msg("Submit"), destination: "here" },
|
||||
{ kind: "back", destination: "bindings" },
|
||||
@@ -214,7 +214,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
.exhaustive();
|
||||
}
|
||||
|
||||
renderInfo(
|
||||
protected renderInfo(
|
||||
state: string,
|
||||
label: string,
|
||||
icons: string[],
|
||||
@@ -235,7 +235,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
</div>`;
|
||||
}
|
||||
|
||||
renderError() {
|
||||
protected renderError() {
|
||||
const { errors } = this.wizard;
|
||||
|
||||
if (Object.keys(errors).length === 0) return nothing;
|
||||
@@ -295,7 +295,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
)}`;
|
||||
}
|
||||
|
||||
renderReview(app: Partial<ApplicationRequest>, provider: OneOfProvider) {
|
||||
protected renderReview(app: Partial<ApplicationRequest>, provider: OneOfProvider) {
|
||||
const renderer = providerRenderers.get(this.wizard.providerModel);
|
||||
if (!renderer) {
|
||||
throw new Error(
|
||||
@@ -338,7 +338,7 @@ export class ApplicationWizardSubmitStep extends CustomEmitterElement(Applicatio
|
||||
`;
|
||||
}
|
||||
|
||||
renderMain() {
|
||||
protected override renderMain() {
|
||||
const app = this.wizard.app;
|
||||
const provider = this.wizard.provider;
|
||||
if (!(this.wizard && app && provider)) {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { AKElement } from "#elements/Base";
|
||||
import { bound } from "#elements/decorators/bound";
|
||||
|
||||
import { msg } from "@lit/localize";
|
||||
import { html } from "lit";
|
||||
@@ -9,13 +8,12 @@ import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
|
||||
@customElement("ak-application-wizard-binding-step-edit-button")
|
||||
export class ApplicationWizardBindingStepEditButton extends AKElement {
|
||||
static styles = [PFButton];
|
||||
public static override styles = [PFButton];
|
||||
|
||||
@property({ type: Number })
|
||||
value = -1;
|
||||
public value = -1;
|
||||
|
||||
@bound
|
||||
onClick(ev: Event) {
|
||||
#clickListener = (ev: Event) => {
|
||||
ev.stopPropagation();
|
||||
this.dispatchEvent(
|
||||
new CustomEvent<number>("click-edit", {
|
||||
@@ -24,10 +22,10 @@ export class ApplicationWizardBindingStepEditButton extends AKElement {
|
||||
detail: this.value,
|
||||
}),
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
return html`<button class="pf-c-button pf-c-secondary" @click=${this.onClick}>
|
||||
public override render() {
|
||||
return html`<button class="pf-c-button pf-c-secondary" @click=${this.#clickListener}>
|
||||
${msg("Edit")}
|
||||
</button>`;
|
||||
}
|
||||
|
||||
@@ -10,16 +10,16 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
@customElement("ak-application-wizard-bindings-toolbar")
|
||||
export class ApplicationWizardBindingsToolbar extends AKElement {
|
||||
static styles = [PFBase, PFButton, PFToolbar];
|
||||
public static override styles = [PFBase, PFButton, PFToolbar];
|
||||
|
||||
@property({ type: Boolean, attribute: "can-delete", reflect: true })
|
||||
canDelete = false;
|
||||
public canDelete = false;
|
||||
|
||||
notify(eventName: string) {
|
||||
protected notify(eventName: string) {
|
||||
this.dispatchEvent(new Event(eventName, { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<div class="pf-c-toolbar">
|
||||
<div class="pf-c-toolbar__content">
|
||||
|
||||
@@ -17,32 +17,32 @@ import { CSSResult } from "lit";
|
||||
import { property, query } from "lit/decorators.js";
|
||||
|
||||
export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKElement {
|
||||
static styles: CSSResult[] = [...AwadStyles];
|
||||
public static override styles: CSSResult[] = [...AwadStyles];
|
||||
|
||||
label = "";
|
||||
public label = "";
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
wizard!: ApplicationWizardState;
|
||||
public wizard!: ApplicationWizardState;
|
||||
|
||||
@property({ type: Object, attribute: false })
|
||||
errors: Record<string | number | symbol, string> = {};
|
||||
public errors: Record<string | number | symbol, string> = {};
|
||||
|
||||
@query("form#providerform")
|
||||
form!: HTMLFormElement;
|
||||
protected form!: HTMLFormElement;
|
||||
|
||||
get formValues() {
|
||||
public get formValues() {
|
||||
return serializeForm([
|
||||
...this.form.querySelectorAll("ak-form-element-horizontal"),
|
||||
...this.form.querySelectorAll("[data-ak-control]"),
|
||||
]);
|
||||
}
|
||||
|
||||
get valid() {
|
||||
public get valid() {
|
||||
this.errors = {};
|
||||
return this.form.checkValidity();
|
||||
}
|
||||
|
||||
errorMessages(name: string) {
|
||||
protected errorMessages(name: string) {
|
||||
return name in this.errors
|
||||
? [this.errors[name]]
|
||||
: (this.wizard.errors?.provider?.[name] ??
|
||||
@@ -50,7 +50,7 @@ export class ApplicationWizardProviderForm<T extends OneOfProvider> extends AKEl
|
||||
[]);
|
||||
}
|
||||
|
||||
isValid(name: keyof T) {
|
||||
protected isValid(name: keyof T) {
|
||||
return !(
|
||||
(this.wizard.errors?.provider?.[name as string] ?? []).length > 0 ||
|
||||
this.errors?.[name] !== undefined
|
||||
|
||||
@@ -17,9 +17,9 @@ import { customElement } from "lit/decorators.js";
|
||||
export class ApplicationWizardLdapProviderForm extends WithBrandConfig(
|
||||
ApplicationWizardProviderForm<LDAPProvider>,
|
||||
) {
|
||||
label = msg("Configure LDAP Provider");
|
||||
public override label = msg("Configure LDAP Provider");
|
||||
|
||||
renderForm(provider: LDAPProvider, errors: ValidationRecord) {
|
||||
protected renderForm(provider: LDAPProvider, errors: ValidationRecord) {
|
||||
return html`
|
||||
<ak-wizard-title>${this.label}</ak-wizard-title>
|
||||
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
@@ -28,7 +28,7 @@ export class ApplicationWizardLdapProviderForm extends WithBrandConfig(
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("LDAP Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -20,15 +20,15 @@ import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-oauth")
|
||||
export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProviderForm<OAuth2ProviderRequest> {
|
||||
label = msg("Configure OAuth2 Provider");
|
||||
public override label = msg("Configure OAuth2 Provider");
|
||||
|
||||
@state()
|
||||
showClientSecret = true;
|
||||
protected showClientSecret = true;
|
||||
|
||||
@state()
|
||||
oauthSources?: PaginatedOAuthSourceList;
|
||||
protected oauthSources?: PaginatedOAuthSourceList;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesOauthList({
|
||||
@@ -40,7 +40,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
|
||||
protected renderForm(provider: OAuth2Provider, errors: ApplicationTransactionValidationError) {
|
||||
const showClientSecretCallback = (show: boolean) => {
|
||||
this.showClientSecret = show;
|
||||
};
|
||||
@@ -55,7 +55,7 @@ export class ApplicationWizardOauth2ProviderForm extends ApplicationWizardProvid
|
||||
</form>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("Oauth2 Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -20,12 +20,12 @@ import { customElement, state } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-proxy")
|
||||
export class ApplicationWizardProxyProviderForm extends ApplicationWizardProviderForm<ProxyProvider> {
|
||||
label = msg("Configure Proxy Provider");
|
||||
public override label = msg("Configure Proxy Provider");
|
||||
|
||||
@state()
|
||||
showHttpBasic = true;
|
||||
protected showHttpBasic = true;
|
||||
|
||||
renderForm(provider: ProxyProvider, errors: ValidationRecord) {
|
||||
protected renderForm(provider: ProxyProvider, errors: ValidationRecord) {
|
||||
const onSetMode: SetMode = (ev: CustomEvent<ProxyModeValue>) => {
|
||||
this.dispatchEvent(
|
||||
new WizardUpdateEvent({ ...this.wizard, proxyMode: ev.detail.value }),
|
||||
@@ -51,7 +51,7 @@ export class ApplicationWizardProxyProviderForm extends ApplicationWizardProvide
|
||||
</form>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("Proxy Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -21,9 +21,9 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-rac")
|
||||
export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderForm<RACProvider> {
|
||||
label = msg("Configure Remote Access Provider");
|
||||
public override label = msg("Configure Remote Access Provider");
|
||||
|
||||
renderForm(provider: RACProvider) {
|
||||
protected renderForm(provider: RACProvider) {
|
||||
return html`
|
||||
<ak-wizard-title>${this.label}</ak-wizard-title>
|
||||
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
@@ -80,7 +80,7 @@ export class ApplicationWizardRACProviderForm extends ApplicationWizardProviderF
|
||||
`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("RAC Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -17,16 +17,16 @@ import { html } from "lit";
|
||||
export class ApplicationWizardRadiusProviderForm extends WithBrandConfig(
|
||||
ApplicationWizardProviderForm<RadiusProvider>,
|
||||
) {
|
||||
label = msg("Configure Radius Provider");
|
||||
public override label = msg("Configure Radius Provider");
|
||||
|
||||
renderForm(provider: RadiusProvider, errors: ValidationRecord) {
|
||||
protected renderForm(provider: RadiusProvider, errors: ValidationRecord) {
|
||||
return html` <ak-wizard-title>${this.label}</ak-wizard-title>
|
||||
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
${renderForm(provider ?? {}, errors, this.brand)}
|
||||
</form>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("RAC Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -14,12 +14,12 @@ import { html } from "lit";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-saml")
|
||||
export class ApplicationWizardProviderSamlForm extends ApplicationWizardProviderForm<SAMLProvider> {
|
||||
label = msg("Configure SAML Provider");
|
||||
public override label = msg("Configure SAML Provider");
|
||||
|
||||
@state()
|
||||
hasSigningKp = false;
|
||||
protected hasSigningKp = false;
|
||||
|
||||
renderForm() {
|
||||
protected renderForm() {
|
||||
const setHasSigningKp = (ev: InputEvent) => {
|
||||
const target = ev.target as AkCryptoCertificateSearch;
|
||||
if (!target) return;
|
||||
@@ -37,7 +37,7 @@ export class ApplicationWizardProviderSamlForm extends ApplicationWizardProvider
|
||||
</form>`;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!(this.wizard.provider && this.wizard.errors)) {
|
||||
throw new Error("SAML Provider Step received uninitialized wizard context.");
|
||||
}
|
||||
|
||||
@@ -13,12 +13,12 @@ import { html } from "lit";
|
||||
|
||||
@customElement("ak-application-wizard-provider-for-scim")
|
||||
export class ApplicationWizardSCIMProvider extends ApplicationWizardProviderForm<SCIMProvider> {
|
||||
label = msg("Configure SCIM Provider");
|
||||
public override label = msg("Configure SCIM Provider");
|
||||
|
||||
@state()
|
||||
propertyMappings?: PaginatedSCIMMappingList;
|
||||
protected propertyMappings?: PaginatedSCIMMappingList;
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`<ak-wizard-title>${this.label}</ak-wizard-title>
|
||||
<form id="providerform" class="pf-c-form pf-m-horizontal" slot="form">
|
||||
${renderForm(
|
||||
|
||||
@@ -30,9 +30,9 @@ enum blueprintSource {
|
||||
@customElement("ak-blueprint-form")
|
||||
export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
@state()
|
||||
source: blueprintSource = blueprintSource.file;
|
||||
protected source: blueprintSource = blueprintSource.file;
|
||||
|
||||
async loadInstance(pk: string): Promise<BlueprintInstance> {
|
||||
protected async loadInstance(pk: string): Promise<BlueprintInstance> {
|
||||
const inst = await new ManagedApi(DEFAULT_CONFIG).managedBlueprintsRetrieve({
|
||||
instanceUuid: pk,
|
||||
});
|
||||
@@ -45,15 +45,15 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
return inst;
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated instance.")
|
||||
: msg("Successfully created instance.");
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFContent];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFContent];
|
||||
|
||||
async send(data: BlueprintInstance): Promise<BlueprintInstance> {
|
||||
protected async send(data: BlueprintInstance): Promise<BlueprintInstance> {
|
||||
if (this.instance?.pk) {
|
||||
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsUpdate({
|
||||
instanceUuid: this.instance.pk,
|
||||
@@ -65,7 +65,7 @@ export class BlueprintForm extends ModelForm<BlueprintInstance, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -46,35 +46,38 @@ export function BlueprintStatus(blueprint?: BlueprintInstance): string {
|
||||
|
||||
@customElement("ak-blueprint-list")
|
||||
export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Blueprints");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg("Automate and template configuration within authentik.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-blueprint";
|
||||
}
|
||||
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<BlueprintInstance>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<BlueprintInstance>> {
|
||||
return new ManagedApi(DEFAULT_CONFIG).managedBlueprintsList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Status"), "status"),
|
||||
@@ -84,7 +87,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Blueprint(s)")}
|
||||
@@ -109,7 +112,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderExpanded(item: BlueprintInstance): TemplateResult {
|
||||
protected override renderExpanded(item: BlueprintInstance): TemplateResult {
|
||||
const [appLabel, modelName] = ModelEnum.AuthentikBlueprintsBlueprintinstance.split(".");
|
||||
return html`<td role="cell" colspan="5">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -145,7 +148,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
</td>`;
|
||||
}
|
||||
|
||||
row(item: BlueprintInstance): TemplateResult[] {
|
||||
protected row(item: BlueprintInstance): TemplateResult[] {
|
||||
let description = undefined;
|
||||
const descKey = "blueprints.goauthentik.io/description";
|
||||
if (
|
||||
@@ -201,7 +204,7 @@ export class BlueprintListPage extends TablePage<BlueprintInstance> {
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
|
||||
@@ -31,19 +31,19 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-brand-form")
|
||||
export class BrandForm extends ModelForm<Brand, string> {
|
||||
loadInstance(pk: string): Promise<Brand> {
|
||||
protected loadInstance(pk: string): Promise<Brand> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreBrandsRetrieve({
|
||||
brandUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated brand.")
|
||||
: msg("Successfully created brand.");
|
||||
}
|
||||
|
||||
async send(data: Brand): Promise<Brand> {
|
||||
protected async send(data: Brand): Promise<Brand> {
|
||||
if (this.instance?.brandUuid) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreBrandsUpdate({
|
||||
brandUuid: this.instance.brandUuid,
|
||||
@@ -55,7 +55,7 @@ export class BrandForm extends ModelForm<Brand, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Domain")} required name="domain">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -19,30 +19,33 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-brand-list")
|
||||
export class BrandListPage extends TablePage<Brand> {
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Brands");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg("Configure visual settings and defaults for different domains.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-tenant";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "domain";
|
||||
public override order = "domain";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Brand>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Brand>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreBrandsList(await this.defaultEndpointConfig());
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Domain"), "domain"),
|
||||
new TableColumn(msg("Brand name"), "branding_title"),
|
||||
@@ -51,7 +54,7 @@ export class BrandListPage extends TablePage<Brand> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Brand(s)")}
|
||||
@@ -76,7 +79,7 @@ export class BrandListPage extends TablePage<Brand> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Brand): TemplateResult[] {
|
||||
protected row(item: Brand): TemplateResult[] {
|
||||
return [
|
||||
html`${item.domain}`,
|
||||
html`${item.brandingTitle}`,
|
||||
@@ -100,7 +103,7 @@ export class BrandListPage extends TablePage<Brand> {
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
|
||||
@@ -44,27 +44,27 @@ export class CoreGroupSearch extends CustomListenerElement(AKElement) {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String, reflect: true })
|
||||
group?: string;
|
||||
public group?: string;
|
||||
|
||||
@query("ak-search-select")
|
||||
search!: SearchSelect<Group>;
|
||||
protected search!: SearchSelect<Group>;
|
||||
|
||||
@property({ type: String })
|
||||
name: string | null | undefined;
|
||||
public name: string | null | undefined;
|
||||
|
||||
selectedGroup?: Group;
|
||||
protected selectedGroup?: Group;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
|
||||
}
|
||||
|
||||
get value() {
|
||||
public get value() {
|
||||
return this.selectedGroup ? renderValue(this.selectedGroup) : undefined;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
|
||||
if (!horizontalContainer) {
|
||||
@@ -77,17 +77,17 @@ export class CoreGroupSearch extends CustomListenerElement(AKElement) {
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchUpdate(ev: CustomEvent) {
|
||||
protected handleSearchUpdate(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this.selectedGroup = ev.detail.value;
|
||||
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
selected(group: Group) {
|
||||
protected selected(group: Group) {
|
||||
return this.group === group.pk;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<ak-search-select
|
||||
.fetchObjects=${fetchObjects}
|
||||
|
||||
@@ -34,13 +34,13 @@ const renderValue = (item: CertificateKeyPair | undefined): string | undefined =
|
||||
@customElement("ak-crypto-certificate-search")
|
||||
export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement) {
|
||||
@property({ type: String, reflect: true })
|
||||
certificate?: string;
|
||||
public certificate?: string;
|
||||
|
||||
@query("ak-search-select")
|
||||
search!: SearchSelect<CertificateKeyPair>;
|
||||
protected search!: SearchSelect<CertificateKeyPair>;
|
||||
|
||||
@property({ type: String })
|
||||
name: string | null | undefined;
|
||||
public name: string | null | undefined;
|
||||
|
||||
/**
|
||||
* Set to `true` to allow certificates without private key to show up. When set to `false`,
|
||||
@@ -48,7 +48,7 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "nokey" })
|
||||
noKey = false;
|
||||
public noKey = false;
|
||||
|
||||
/**
|
||||
* Set this to true if, should there be only one certificate available, you want the system to
|
||||
@@ -57,22 +57,22 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean, attribute: "singleton" })
|
||||
singleton = false;
|
||||
public singleton = false;
|
||||
|
||||
selectedKeypair?: CertificateKeyPair;
|
||||
public selectedKeypair?: CertificateKeyPair;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
this.fetchObjects = this.fetchObjects.bind(this);
|
||||
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
|
||||
}
|
||||
|
||||
get value() {
|
||||
public get value() {
|
||||
return this.selectedKeypair ? renderValue(this.selectedKeypair) : null;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
|
||||
if (!horizontalContainer) {
|
||||
@@ -85,13 +85,13 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
}
|
||||
}
|
||||
|
||||
handleSearchUpdate(ev: CustomEvent) {
|
||||
protected handleSearchUpdate(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this.selectedKeypair = ev.detail.value;
|
||||
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
async fetchObjects(query?: string): Promise<CertificateKeyPair[]> {
|
||||
protected async fetchObjects(query?: string): Promise<CertificateKeyPair[]> {
|
||||
const args: CryptoCertificatekeypairsListRequest = {
|
||||
ordering: "name",
|
||||
hasKey: !this.noKey,
|
||||
@@ -106,14 +106,14 @@ export class AkCryptoCertificateSearch extends CustomListenerElement(AKElement)
|
||||
return certificates.results;
|
||||
}
|
||||
|
||||
selected(item: CertificateKeyPair, items: CertificateKeyPair[]) {
|
||||
protected selected(item: CertificateKeyPair, items: CertificateKeyPair[]) {
|
||||
return (
|
||||
(this.singleton && !this.certificate && items.length === 1) ||
|
||||
(!!this.certificate && this.certificate === item.pk)
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<ak-search-select
|
||||
name=${ifDefined(this.name ?? undefined)}
|
||||
|
||||
@@ -43,7 +43,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
flowType?: FlowsInstancesListDesignationEnum;
|
||||
public flowType?: FlowsInstancesListDesignationEnum;
|
||||
|
||||
/**
|
||||
* The id of the current flow, if any. For stages where the flow is already defined.
|
||||
@@ -51,7 +51,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
currentFlow?: string | undefined;
|
||||
public currentFlow?: string | undefined;
|
||||
|
||||
/**
|
||||
* If true, it is not valid to leave the flow blank.
|
||||
@@ -59,10 +59,10 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: Boolean })
|
||||
required?: boolean = false;
|
||||
public required?: boolean = false;
|
||||
|
||||
@query("ak-search-select")
|
||||
search!: SearchSelect<T>;
|
||||
protected search!: SearchSelect<T>;
|
||||
|
||||
/**
|
||||
* When specified and the object instance does not have a flow selected, auto-select the flow with the given slug.
|
||||
@@ -70,31 +70,31 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
* @attr
|
||||
*/
|
||||
@property()
|
||||
defaultFlowSlug?: string;
|
||||
public defaultFlowSlug?: string;
|
||||
|
||||
@property({ type: String })
|
||||
name: string | null | undefined;
|
||||
public name: string | null | undefined;
|
||||
|
||||
selectedFlow?: T;
|
||||
protected selectedFlow?: T;
|
||||
|
||||
get value() {
|
||||
public get value() {
|
||||
return this.selectedFlow ? getFlowValue(this.selectedFlow) : null;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.fetchObjects = this.fetchObjects.bind(this);
|
||||
this.selected = this.selected.bind(this);
|
||||
this.handleSearchUpdate = this.handleSearchUpdate.bind(this);
|
||||
}
|
||||
|
||||
handleSearchUpdate(ev: CustomEvent) {
|
||||
protected handleSearchUpdate(ev: CustomEvent) {
|
||||
ev.stopPropagation();
|
||||
this.selectedFlow = ev.detail.value;
|
||||
this.dispatchEvent(new InputEvent("input", { bubbles: true, composed: true }));
|
||||
}
|
||||
|
||||
async fetchObjects(query?: string): Promise<Flow[]> {
|
||||
protected async fetchObjects(query?: string): Promise<Flow[]> {
|
||||
const args: FlowsInstancesListRequest = {
|
||||
ordering: "slug",
|
||||
designation: this.flowType,
|
||||
@@ -108,7 +108,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
* use this method, but several have more complex needs, such as relating to the brand, or just
|
||||
* returning false.
|
||||
*/
|
||||
selected(flow: Flow): boolean {
|
||||
protected selected(flow: Flow): boolean {
|
||||
let selected = this.currentFlow === flow.pk;
|
||||
if (!this.currentFlow && this.defaultFlowSlug && flow.slug === this.defaultFlowSlug) {
|
||||
selected = true;
|
||||
@@ -116,7 +116,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
return selected;
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
public override connectedCallback() {
|
||||
super.connectedCallback();
|
||||
const horizontalContainer = this.closest("ak-form-element-horizontal[name]");
|
||||
if (!horizontalContainer) {
|
||||
@@ -129,7 +129,7 @@ export class FlowSearch<T extends Flow> extends CustomListenerElement(AKElement)
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<ak-search-select
|
||||
.fetchObjects=${this.fetchObjects}
|
||||
|
||||
@@ -19,14 +19,14 @@ export class AkBrandedFlowSearch<T extends Flow> extends FlowSearch<T> {
|
||||
* @attr
|
||||
*/
|
||||
@property({ attribute: false, type: String })
|
||||
brandFlow?: string;
|
||||
public brandFlow?: string;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
}
|
||||
|
||||
selected(flow: Flow): boolean {
|
||||
public override selected(flow: Flow): boolean {
|
||||
return super.selected(flow) || flow.pk === this.brandFlow;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-flow-search-no-default")
|
||||
export class AkFlowSearchNoDefault<T extends Flow> extends FlowSearch<T> {
|
||||
render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<ak-search-select
|
||||
.fetchObjects=${this.fetchObjects}
|
||||
|
||||
@@ -20,7 +20,7 @@ export class AkSourceFlowSearch<T extends Flow> extends FlowSearch<T> {
|
||||
*/
|
||||
|
||||
@property({ type: String })
|
||||
fallback: string | undefined;
|
||||
public fallback: string | undefined;
|
||||
|
||||
/**
|
||||
* The primary key of the Source (not the Flow). Mostly the instancePk itself, used to affirm
|
||||
@@ -29,16 +29,16 @@ export class AkSourceFlowSearch<T extends Flow> extends FlowSearch<T> {
|
||||
* @attr
|
||||
*/
|
||||
@property({ type: String })
|
||||
instanceId: string | undefined;
|
||||
public instanceId: string | undefined;
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.selected = this.selected.bind(this);
|
||||
}
|
||||
|
||||
// If there's no instance or no currentFlowId for it and the flow resembles the fallback,
|
||||
// otherwise defer to the parent class.
|
||||
selected(flow: Flow): boolean {
|
||||
public override selected(flow: Flow): boolean {
|
||||
return (
|
||||
(!this.instanceId && !this.currentFlow && flow.slug === this.fallback) ||
|
||||
super.selected(flow)
|
||||
|
||||
@@ -11,7 +11,7 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-license-notice")
|
||||
export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
|
||||
static styles = [$PFBase];
|
||||
public static styles = [$PFBase];
|
||||
|
||||
@property()
|
||||
public label = msg("Enterprise only");
|
||||
@@ -19,7 +19,7 @@ export class AKLicenceNotice extends WithLicenseSummary(AKElement) {
|
||||
@property()
|
||||
public description = msg("Learn more about the enterprise license.");
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (this.hasEnterpriseLicense) {
|
||||
return nothing;
|
||||
}
|
||||
|
||||
@@ -17,17 +17,17 @@ import { customElement } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-crypto-certificate-generate-form")
|
||||
export class CertificateKeyPairForm extends Form<CertificateGenerationRequest> {
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully generated certificate-key pair.");
|
||||
}
|
||||
|
||||
async send(data: CertificateGenerationRequest): Promise<CertificateKeyPair> {
|
||||
protected async send(data: CertificateGenerationRequest): Promise<CertificateKeyPair> {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsGenerateCreate({
|
||||
certificateGenerationRequest: data,
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal
|
||||
label=${msg("Common Name")}
|
||||
name="commonName"
|
||||
|
||||
@@ -15,19 +15,19 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-crypto-certificate-form")
|
||||
export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string> {
|
||||
loadInstance(pk: string): Promise<CertificateKeyPair> {
|
||||
protected loadInstance(pk: string): Promise<CertificateKeyPair> {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsRetrieve({
|
||||
kpUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated certificate-key pair.")
|
||||
: msg("Successfully created certificate-key pair.");
|
||||
}
|
||||
|
||||
async send(data: CertificateKeyPair): Promise<CertificateKeyPair> {
|
||||
protected async send(data: CertificateKeyPair): Promise<CertificateKeyPair> {
|
||||
if (this.instance) {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsPartialUpdate({
|
||||
kpUuid: this.instance.pk || "",
|
||||
@@ -39,7 +39,7 @@ export class CertificateKeyPairForm extends ModelForm<CertificateKeyPair, string
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} name="name" required>
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -27,37 +27,40 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
|
||||
|
||||
@customElement("ak-crypto-certificate-list")
|
||||
export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Certificate-Key Pairs");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg(
|
||||
"Import certificates of external providers or create certificates to sign requests with.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-key";
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<CertificateKeyPair>> {
|
||||
return new CryptoApi(DEFAULT_CONFIG).cryptoCertificatekeypairsList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Private key available?")),
|
||||
@@ -66,7 +69,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Certificate-Key Pair(s)")}
|
||||
@@ -94,7 +97,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: CertificateKeyPair): TemplateResult[] {
|
||||
protected row(item: CertificateKeyPair): TemplateResult[] {
|
||||
let managedSubText = msg("Managed by authentik");
|
||||
if (item.managed && item.managed.startsWith("goauthentik.io/crypto/discovered")) {
|
||||
managedSubText = msg("Managed by authentik (Discovered)");
|
||||
@@ -140,7 +143,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: CertificateKeyPair): TemplateResult {
|
||||
protected override renderExpanded(item: CertificateKeyPair): TemplateResult {
|
||||
return html`<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
@@ -210,7 +213,7 @@ export class CertificateKeyPairListPage extends TablePage<CertificateKeyPair> {
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Import")} </span>
|
||||
|
||||
@@ -17,27 +17,27 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@customElement("ak-enterprise-license-form")
|
||||
export class EnterpriseLicenseForm extends ModelForm<License, string> {
|
||||
@state()
|
||||
installID?: string;
|
||||
protected installID?: string;
|
||||
|
||||
loadInstance(pk: string): Promise<License> {
|
||||
protected loadInstance(pk: string): Promise<License> {
|
||||
return new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseRetrieve({
|
||||
licenseUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated license.")
|
||||
: msg("Successfully created license.");
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
public override async load(): Promise<void> {
|
||||
this.installID = (
|
||||
await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseInstallIdRetrieve()
|
||||
).installId;
|
||||
}
|
||||
|
||||
async send(data: License): Promise<License> {
|
||||
protected async send(data: License): Promise<License> {
|
||||
return (
|
||||
this.instance
|
||||
? new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicensePartialUpdate({
|
||||
@@ -53,7 +53,7 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Install ID")}>
|
||||
<input
|
||||
class="pf-c-form-control pf-m-monospace"
|
||||
|
||||
@@ -36,35 +36,38 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
|
||||
@customElement("ak-enterprise-license-list")
|
||||
export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Licenses");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg("Manage enterprise licenses");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-key";
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
@state()
|
||||
forecast?: LicenseForecast;
|
||||
protected forecast?: LicenseForecast;
|
||||
|
||||
@state()
|
||||
summary?: LicenseSummary;
|
||||
protected summary?: LicenseSummary;
|
||||
|
||||
@state()
|
||||
installID?: string;
|
||||
protected installID?: string;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
PFGrid,
|
||||
PFBanner,
|
||||
@@ -81,7 +84,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
`,
|
||||
];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<License>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<License>> {
|
||||
this.forecast = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseForecastRetrieve();
|
||||
this.summary = await new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve({
|
||||
cached: false,
|
||||
@@ -94,7 +97,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Users")),
|
||||
@@ -105,7 +108,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
|
||||
// TODO: Make this more generic, maybe automatically get the plural name
|
||||
// of the object to use in the renderEmpty
|
||||
renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
protected override renderEmpty(inner?: TemplateResult): TemplateResult {
|
||||
return super.renderEmpty(html`
|
||||
${inner
|
||||
? inner
|
||||
@@ -119,7 +122,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
`);
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("License(s)")}
|
||||
@@ -147,7 +150,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderSectionBefore(): TemplateResult {
|
||||
protected override renderSectionBefore(): TemplateResult {
|
||||
return html`
|
||||
<section class="pf-c-page__main-section pf-m-no-padding-bottom">
|
||||
<div
|
||||
@@ -199,7 +202,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
`;
|
||||
}
|
||||
|
||||
row(item: License): TemplateResult[] {
|
||||
protected row(item: License): TemplateResult[] {
|
||||
let color = PFColor.Green;
|
||||
if (item.expiry) {
|
||||
const now = new Date();
|
||||
@@ -236,7 +239,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
];
|
||||
}
|
||||
|
||||
renderGetLicenseCard() {
|
||||
protected renderGetLicenseCard() {
|
||||
const renderSpinner = () =>
|
||||
html` <div class="pf-c-card__body">
|
||||
<ak-spinner></ak-spinner>
|
||||
@@ -273,7 +276,7 @@ export class EnterpriseLicenseListPage extends TablePage<License> {
|
||||
</div> `;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Install")} </span>
|
||||
|
||||
@@ -16,14 +16,20 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-enterprise-status-card")
|
||||
export class EnterpriseStatusCard extends AKElement {
|
||||
@state()
|
||||
forecast?: LicenseForecast;
|
||||
protected forecast?: LicenseForecast;
|
||||
|
||||
@state()
|
||||
summary?: LicenseSummary;
|
||||
protected summary?: LicenseSummary;
|
||||
|
||||
static styles: CSSResult[] = [PFBase, PFDescriptionList, PFCard, PFSplit, PFProgress];
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFDescriptionList,
|
||||
PFCard,
|
||||
PFSplit,
|
||||
PFProgress,
|
||||
];
|
||||
|
||||
renderSummaryBadge() {
|
||||
protected renderSummaryBadge() {
|
||||
switch (this.summary?.status) {
|
||||
case LicenseSummaryStatusEnum.Expired:
|
||||
return html`<ak-label color=${PFColor.Red}>${msg("Expired")}</ak-label>`;
|
||||
@@ -40,13 +46,13 @@ export class EnterpriseStatusCard extends AKElement {
|
||||
}
|
||||
}
|
||||
|
||||
calcUserPercentage(licensed: number, current: number) {
|
||||
protected calcUserPercentage(licensed: number, current: number) {
|
||||
const percentage = licensed > 0 ? Math.ceil(current / (licensed / 100)) : 0;
|
||||
if (current > 0 && licensed === 0) return Infinity;
|
||||
return percentage;
|
||||
}
|
||||
|
||||
render() {
|
||||
public override render() {
|
||||
if (!this.forecast || !this.summary) {
|
||||
return html`${msg("Loading")}`;
|
||||
}
|
||||
|
||||
@@ -26,26 +26,29 @@ import PFGrid from "@patternfly/patternfly/layouts/Grid/grid.css";
|
||||
|
||||
@customElement("ak-event-list")
|
||||
export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
|
||||
expandable = true;
|
||||
supportsQL = true;
|
||||
public override expandable = true;
|
||||
public override supportsQL = true;
|
||||
|
||||
pageTitle(): string {
|
||||
protected pageTitle(): string {
|
||||
return msg("Event Log");
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
|
||||
protected pageDescription(): string | undefined {
|
||||
return;
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-catalog";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "-created";
|
||||
public override order = "-created";
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static styles: CSSResult[] = [
|
||||
...TablePage.styles,
|
||||
PFGrid,
|
||||
css`
|
||||
@@ -55,11 +58,11 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
|
||||
`,
|
||||
];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Event>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Event>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsList(await this.defaultEndpointConfig());
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Action"), "action"),
|
||||
new TableColumn(msg("User"), "user"),
|
||||
@@ -70,7 +73,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
|
||||
];
|
||||
}
|
||||
|
||||
renderSectionBefore(): TemplateResult {
|
||||
protected override renderSectionBefore(): TemplateResult {
|
||||
if (this.hasEnterpriseLicense) {
|
||||
return html`<div
|
||||
class="pf-l-grid pf-m-gutter pf-c-page__main-section pf-m-no-padding-bottom"
|
||||
@@ -104,7 +107,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: EventWithContext): SlottedTemplateResult[] {
|
||||
protected row(item: EventWithContext): SlottedTemplateResult[] {
|
||||
return [
|
||||
html`<div>${actionToLabel(item.action)}</div>
|
||||
<small>${item.app}</small>`,
|
||||
@@ -122,7 +125,7 @@ export class EventListPage extends WithLicenseSummary(TablePage<Event>) {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: Event): TemplateResult {
|
||||
protected override renderExpanded(item: Event): TemplateResult {
|
||||
return html` <td role="cell" colspan="5">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<ak-event-info .event=${item as EventWithContext}></ak-event-info>
|
||||
|
||||
@@ -28,7 +28,7 @@ import OL from "ol/ol.css";
|
||||
|
||||
@customElement("ak-map")
|
||||
export class Map extends OlMap {
|
||||
public render() {
|
||||
public override render() {
|
||||
return html`
|
||||
<style>
|
||||
${OL}
|
||||
@@ -57,18 +57,18 @@ export class Map extends OlMap {
|
||||
@customElement("ak-events-map")
|
||||
export class EventMap extends AKElement {
|
||||
@property({ attribute: false })
|
||||
events?: PaginatedResponse<Event>;
|
||||
public events?: PaginatedResponse<Event>;
|
||||
|
||||
@query("ol-layer-vector")
|
||||
vectorLayer?: OlLayerVector;
|
||||
protected vectorLayer?: OlLayerVector;
|
||||
|
||||
@query("ak-map")
|
||||
map?: Map;
|
||||
protected map?: Map;
|
||||
|
||||
@property({ type: Number })
|
||||
zoomPaddingPx = 100;
|
||||
public zoomPaddingPx = 100;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFCard,
|
||||
css`
|
||||
@@ -82,7 +82,7 @@ export class EventMap extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
updated(_changedProperties: PropertyValues<this>): void {
|
||||
public override updated(_changedProperties: PropertyValues<this>): void {
|
||||
if (!_changedProperties.has("events")) {
|
||||
return;
|
||||
}
|
||||
@@ -141,7 +141,7 @@ export class EventMap extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<ak-map>
|
||||
<ol-select
|
||||
|
||||
@@ -26,26 +26,33 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-event-view")
|
||||
export class EventViewPage extends AKElement {
|
||||
@property({ type: String })
|
||||
eventID?: string;
|
||||
public eventID?: string;
|
||||
|
||||
@state()
|
||||
event!: EventWithContext;
|
||||
protected event!: EventWithContext;
|
||||
|
||||
static styles: CSSResult[] = [PFBase, PFGrid, PFDescriptionList, PFPage, PFContent, PFCard];
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFGrid,
|
||||
PFDescriptionList,
|
||||
PFPage,
|
||||
PFContent,
|
||||
PFCard,
|
||||
];
|
||||
|
||||
fetchEvent(eventUuid: string) {
|
||||
protected fetchEvent(eventUuid: string) {
|
||||
new EventsApi(DEFAULT_CONFIG).eventsEventsRetrieve({ eventUuid }).then((ev) => {
|
||||
this.event = ev as EventWithContext;
|
||||
});
|
||||
}
|
||||
|
||||
willUpdate(changedProperties: PropertyValues<this>) {
|
||||
public override willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("eventID") && this.eventID) {
|
||||
this.fetchEvent(this.eventID);
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
if (!this.event) {
|
||||
return html`<ak-page-header icon="pf-icon pf-icon-catalog" header=${msg("Loading")}>
|
||||
</ak-page-header> `;
|
||||
|
||||
@@ -14,18 +14,18 @@ import PFCard from "@patternfly/patternfly/components/Card/card.css";
|
||||
@customElement("ak-events-volume-chart")
|
||||
export class EventVolumeChart extends EventChart {
|
||||
@property({ attribute: "with-map", type: Boolean })
|
||||
withMap = false;
|
||||
public withMap = false;
|
||||
|
||||
_query?: EventsEventsListRequest;
|
||||
#query?: EventsEventsListRequest;
|
||||
|
||||
@property({ attribute: false })
|
||||
set query(value: EventsEventsListRequest | undefined) {
|
||||
if (JSON.stringify(value) !== JSON.stringify(this._query)) return;
|
||||
this._query = value;
|
||||
public set query(value: EventsEventsListRequest | undefined) {
|
||||
if (JSON.stringify(value) !== JSON.stringify(this.#query)) return;
|
||||
this.#query = value;
|
||||
this.refreshHandler();
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
PFCard,
|
||||
css`
|
||||
@@ -38,20 +38,20 @@ export class EventVolumeChart extends EventChart {
|
||||
`,
|
||||
];
|
||||
|
||||
apiRequest(): Promise<EventVolume[]> {
|
||||
protected apiRequest(): Promise<EventVolume[]> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsEventsVolumeList({
|
||||
historyDays: 7,
|
||||
...this._query,
|
||||
...this.#query,
|
||||
});
|
||||
}
|
||||
|
||||
getChartData(data: EventVolume[]): ChartData {
|
||||
protected getChartData(data: EventVolume[]): ChartData {
|
||||
return this.eventVolume(data, {
|
||||
padToDays: 7,
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<div class="pf-c-card">
|
||||
<div class="pf-c-card__body">${super.render()}</div>
|
||||
</div>`;
|
||||
|
||||
@@ -27,27 +27,27 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-event-rule-form")
|
||||
export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
eventTransports?: PaginatedNotificationTransportList;
|
||||
protected eventTransports?: PaginatedNotificationTransportList;
|
||||
|
||||
loadInstance(pk: string): Promise<NotificationRule> {
|
||||
protected loadInstance(pk: string): Promise<NotificationRule> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesRetrieve({
|
||||
pbmUuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
public override async load(): Promise<void> {
|
||||
this.eventTransports = await new EventsApi(DEFAULT_CONFIG).eventsTransportsList({
|
||||
ordering: "name",
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated rule.")
|
||||
: msg("Successfully created rule.");
|
||||
}
|
||||
|
||||
async send(data: NotificationRule): Promise<NotificationRule> {
|
||||
protected async send(data: NotificationRule): Promise<NotificationRule> {
|
||||
if (this.instance) {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesUpdate({
|
||||
pbmUuid: this.instance.pk || "",
|
||||
@@ -59,7 +59,7 @@ export class RuleForm extends ModelForm<NotificationRule, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -27,33 +27,36 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-event-rule-list")
|
||||
export class RuleListPage extends TablePage<NotificationRule> {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Notification Rules");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg(
|
||||
"Send notifications whenever a specific Event is created and matched by policies.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-attention-bell";
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<NotificationRule>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<NotificationRule>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsRulesList(await this.defaultEndpointConfig());
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Enabled")),
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
@@ -63,7 +66,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Notification rule(s)")}
|
||||
@@ -85,7 +88,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: NotificationRule): TemplateResult[] {
|
||||
protected row(item: NotificationRule): TemplateResult[] {
|
||||
const enabled = !!item.destinationGroupObj || item.destinationEventUser;
|
||||
return [
|
||||
html`<ak-status-label type="warning" ?good=${enabled}></ak-status-label>`,
|
||||
@@ -115,7 +118,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
@@ -126,7 +129,7 @@ export class RuleListPage extends TablePage<NotificationRule> {
|
||||
`;
|
||||
}
|
||||
|
||||
renderExpanded(item: NotificationRule): TemplateResult {
|
||||
protected override renderExpanded(item: NotificationRule): TemplateResult {
|
||||
const [appLabel, modelName] = ModelEnum.AuthentikEventsNotificationrule.split(".");
|
||||
return html` <td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
|
||||
@@ -23,7 +23,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-event-transport-form")
|
||||
export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
loadInstance(pk: string): Promise<NotificationTransport> {
|
||||
protected loadInstance(pk: string): Promise<NotificationTransport> {
|
||||
return new EventsApi(DEFAULT_CONFIG)
|
||||
.eventsTransportsRetrieve({
|
||||
uuid: pk,
|
||||
@@ -35,15 +35,15 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
showWebhook = false;
|
||||
public showWebhook = false;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated transport.")
|
||||
: msg("Successfully created transport.");
|
||||
}
|
||||
|
||||
async send(data: NotificationTransport): Promise<NotificationTransport> {
|
||||
protected async send(data: NotificationTransport): Promise<NotificationTransport> {
|
||||
if (this.instance) {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
@@ -55,7 +55,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
});
|
||||
}
|
||||
|
||||
onModeChange(mode: string | undefined): void {
|
||||
protected onModeChange(mode: string | undefined): void {
|
||||
if (
|
||||
mode === NotificationTransportModeEnum.Webhook ||
|
||||
mode === NotificationTransportModeEnum.WebhookSlack
|
||||
@@ -66,7 +66,7 @@ export class TransportForm extends ModelForm<NotificationTransport, string> {
|
||||
}
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -25,33 +25,36 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-event-transport-list")
|
||||
export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Notification Transports");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg("Define how notifications are sent to users, like Email or Webhook.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-export";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<NotificationTransport>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<NotificationTransport>> {
|
||||
return new EventsApi(DEFAULT_CONFIG).eventsTransportsList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Mode"), "mode"),
|
||||
@@ -59,7 +62,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Notification transport(s)")}
|
||||
@@ -81,7 +84,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: NotificationTransport): TemplateResult[] {
|
||||
protected row(item: NotificationTransport): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.modeVerbose}`,
|
||||
@@ -117,7 +120,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: NotificationTransport): TemplateResult {
|
||||
protected override renderExpanded(item: NotificationTransport): TemplateResult {
|
||||
const [appLabel, modelName] = ModelEnum.AuthentikEventsNotificationtransport.split(".");
|
||||
return html`<td role="cell" colspan="5">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -141,7 +144,7 @@ export class TransportListPage extends TablePage<NotificationTransport> {
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
|
||||
@@ -24,23 +24,23 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-bound-stages-list")
|
||||
export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
order = "order";
|
||||
public override order = "order";
|
||||
|
||||
@property()
|
||||
target?: string;
|
||||
public target?: string;
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<FlowStageBinding>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<FlowStageBinding>> {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
target: this.target || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Order"), "order"),
|
||||
new TableColumn(msg("Name"), "stage__name"),
|
||||
@@ -49,7 +49,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Stage binding(s)")}
|
||||
@@ -77,7 +77,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: FlowStageBinding): TemplateResult[] {
|
||||
protected row(item: FlowStageBinding): TemplateResult[] {
|
||||
return [
|
||||
html`<pre>${item.order}</pre>`,
|
||||
html`${item.stageObj?.name}`,
|
||||
@@ -114,7 +114,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: FlowStageBinding): TemplateResult {
|
||||
protected override renderExpanded(item: FlowStageBinding): TemplateResult {
|
||||
return html` <td></td>
|
||||
<td role="cell" colspan="4">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -134,7 +134,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderEmpty(): TemplateResult {
|
||||
protected override renderEmpty(): TemplateResult {
|
||||
return super.renderEmpty(
|
||||
html`<ak-empty-state icon="pf-icon-module">
|
||||
<span>${msg("No Stages bound")}</span>
|
||||
@@ -159,7 +159,7 @@ export class BoundStagesList extends Table<FlowStageBinding> {
|
||||
);
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-stage-wizard
|
||||
createText=${msg("Create and bind Stage")}
|
||||
|
||||
@@ -11,9 +11,9 @@ import { customElement, property } from "lit/decorators.js";
|
||||
@customElement("ak-flow-diagram")
|
||||
export class FlowDiagram extends Diagram {
|
||||
@property()
|
||||
flowSlug?: string;
|
||||
public flowSlug?: string;
|
||||
|
||||
refreshHandler = (): void => {
|
||||
protected override refreshHandler = (): void => {
|
||||
this.diagram = undefined;
|
||||
new FlowsApi(DEFAULT_CONFIG)
|
||||
.flowsInstancesDiagramRetrieve({
|
||||
|
||||
@@ -27,7 +27,7 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-flow-form")
|
||||
export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
async loadInstance(pk: string): Promise<Flow> {
|
||||
protected async loadInstance(pk: string): Promise<Flow> {
|
||||
const flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({
|
||||
slug: pk,
|
||||
});
|
||||
@@ -35,16 +35,16 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
return flow;
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated flow.")
|
||||
: msg("Successfully created flow.");
|
||||
}
|
||||
|
||||
@property({ type: Boolean })
|
||||
clearBackground = false;
|
||||
public clearBackground = false;
|
||||
|
||||
async send(data: Flow): Promise<void | Flow> {
|
||||
protected async send(data: Flow): Promise<void | Flow> {
|
||||
let flow: Flow;
|
||||
if (this.instance) {
|
||||
flow = await new FlowsApi(DEFAULT_CONFIG).flowsInstancesUpdate({
|
||||
@@ -77,7 +77,7 @@ export class FlowForm extends WithCapabilitiesConfig(ModelForm<Flow, string>) {
|
||||
return flow;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -18,15 +18,15 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
|
||||
@customElement("ak-flow-import-form")
|
||||
export class FlowImportForm extends Form<Flow> {
|
||||
@state()
|
||||
result?: FlowImportResult;
|
||||
protected result?: FlowImportResult;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully imported flow.");
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
async send(): Promise<FlowImportResult> {
|
||||
protected async send(): Promise<FlowImportResult> {
|
||||
const file = this.files().get("flow");
|
||||
if (!file) {
|
||||
throw new SentryIgnoredError("No form data");
|
||||
@@ -41,7 +41,7 @@ export class FlowImportForm extends Form<Flow> {
|
||||
return result;
|
||||
}
|
||||
|
||||
renderResult(): TemplateResult {
|
||||
protected renderResult(): TemplateResult {
|
||||
return html`
|
||||
<ak-form-element-horizontal label=${msg("Successful")}>
|
||||
<div class="pf-c-form__group-label">
|
||||
@@ -64,7 +64,7 @@ export class FlowImportForm extends Form<Flow> {
|
||||
`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("Flow")} name="flow">
|
||||
<input type="file" value="" class="pf-c-form-control" />
|
||||
<p class="pf-c-form__helper-text">
|
||||
|
||||
@@ -22,38 +22,41 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-flow-list")
|
||||
export class FlowListPage extends TablePage<Flow> {
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Flows");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg(
|
||||
"Flows describe a chain of Stages to authenticate, enroll or recover a user. Stages are chosen based on policies applied to them.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-process-automation";
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "slug";
|
||||
public override order = "slug";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Flow>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Flow>> {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesList(await this.defaultEndpointConfig());
|
||||
}
|
||||
|
||||
groupBy(items: Flow[]): [string, Flow[]][] {
|
||||
public override groupBy(items: Flow[]): [string, Flow[]][] {
|
||||
return groupBy(items, (flow) => {
|
||||
return DesignationToLabel(flow.designation);
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Identifier"), "slug"),
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
@@ -63,7 +66,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Flow(s)")}
|
||||
@@ -85,7 +88,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Flow): TemplateResult[] {
|
||||
protected row(item: Flow): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<a href="#/flow/flows/${item.slug}">
|
||||
@@ -127,7 +130,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
@@ -144,7 +147,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
${super.renderToolbar()}
|
||||
<ak-forms-confirm
|
||||
|
||||
@@ -32,12 +32,12 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-flow-view")
|
||||
export class FlowViewPage extends AKElement {
|
||||
@property({ type: String })
|
||||
flowSlug?: string;
|
||||
public flowSlug?: string;
|
||||
|
||||
@state()
|
||||
flow!: Flow;
|
||||
protected flow!: Flow;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFDescriptionList,
|
||||
@@ -55,19 +55,19 @@ export class FlowViewPage extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
fetchFlow(slug: string) {
|
||||
new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({ slug }).then((flow) => {
|
||||
protected fetchFlow(slug: string) {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsInstancesRetrieve({ slug }).then((flow) => {
|
||||
this.flow = flow;
|
||||
});
|
||||
}
|
||||
|
||||
willUpdate(changedProperties: PropertyValues<this>) {
|
||||
public override willUpdate(changedProperties: PropertyValues<this>) {
|
||||
if (changedProperties.has("flowSlug") && this.flowSlug) {
|
||||
this.fetchFlow(this.flowSlug);
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
if (!this.flow) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ import { customElement, property, state } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-stage-binding-form")
|
||||
export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
async loadInstance(pk: string): Promise<FlowStageBinding> {
|
||||
protected async loadInstance(pk: string): Promise<FlowStageBinding> {
|
||||
const binding = await new FlowsApi(DEFAULT_CONFIG).flowsBindingsRetrieve({
|
||||
fsbUuid: pk,
|
||||
});
|
||||
@@ -33,19 +33,19 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
}
|
||||
|
||||
@property()
|
||||
targetPk?: string;
|
||||
public targetPk?: string;
|
||||
|
||||
@state()
|
||||
defaultOrder = 0;
|
||||
protected defaultOrder = 0;
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
if (this.instance?.pk) {
|
||||
return msg("Successfully updated binding.");
|
||||
}
|
||||
return msg("Successfully created binding.");
|
||||
}
|
||||
|
||||
send(data: FlowStageBinding): Promise<unknown> {
|
||||
protected send(data: FlowStageBinding): Promise<unknown> {
|
||||
if (this.instance?.pk) {
|
||||
return new FlowsApi(DEFAULT_CONFIG).flowsBindingsPartialUpdate({
|
||||
fsbUuid: this.instance.pk,
|
||||
@@ -60,7 +60,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
});
|
||||
}
|
||||
|
||||
async getOrder(): Promise<number> {
|
||||
protected async getOrder(): Promise<number> {
|
||||
if (this.instance?.pk) {
|
||||
return this.instance.order;
|
||||
}
|
||||
@@ -74,7 +74,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
return Math.max(...orders) + 1;
|
||||
}
|
||||
|
||||
renderTarget(): TemplateResult {
|
||||
protected renderTarget(): TemplateResult {
|
||||
if (this.instance?.target || this.targetPk) {
|
||||
return html``;
|
||||
}
|
||||
@@ -87,7 +87,7 @@ export class StageBindingForm extends ModelForm<FlowStageBinding, string> {
|
||||
</ak-form-element-horizontal>`;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` ${this.renderTarget()}
|
||||
<ak-form-element-horizontal label=${msg("Stage")} required name="stage">
|
||||
<ak-search-select
|
||||
|
||||
@@ -27,7 +27,7 @@ export function rbacRolePair(item: Role): DualSelectPair {
|
||||
|
||||
@customElement("ak-group-form")
|
||||
export class GroupForm extends ModelForm<Group, string> {
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
...super.styles,
|
||||
css`
|
||||
.pf-c-button.pf-m-control {
|
||||
@@ -39,20 +39,20 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
`,
|
||||
];
|
||||
|
||||
loadInstance(pk: string): Promise<Group> {
|
||||
protected loadInstance(pk: string): Promise<Group> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsRetrieve({
|
||||
groupUuid: pk,
|
||||
includeUsers: false,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated group.")
|
||||
: msg("Successfully created group.");
|
||||
}
|
||||
|
||||
async send(data: Group): Promise<Group> {
|
||||
protected async send(data: Group): Promise<Group> {
|
||||
if (this.instance?.pk) {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsPartialUpdate({
|
||||
groupUuid: this.instance.pk,
|
||||
@@ -65,7 +65,7 @@ export class GroupForm extends ModelForm<Group, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -18,32 +18,36 @@ import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
@customElement("ak-group-list")
|
||||
export class GroupListPage extends TablePage<Group> {
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
searchEnabled(): boolean {
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
|
||||
protected pageTitle(): string {
|
||||
return msg("Groups");
|
||||
}
|
||||
pageDescription(): string {
|
||||
|
||||
protected pageDescription(): string {
|
||||
return msg("Group users together and give them permissions based on the membership.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-users";
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Group>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Group>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
includeUsers: false,
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Parent"), "parent"),
|
||||
@@ -53,7 +57,7 @@ export class GroupListPage extends TablePage<Group> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Group(s)")}
|
||||
@@ -75,7 +79,7 @@ export class GroupListPage extends TablePage<Group> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Group): TemplateResult[] {
|
||||
protected row(item: Group): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/identity/groups/${item.pk}">${item.name}</a>`,
|
||||
html`${item.parentName || msg("-")}`,
|
||||
@@ -94,7 +98,7 @@ export class GroupListPage extends TablePage<Group> {
|
||||
];
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
|
||||
@@ -35,7 +35,7 @@ import PFSizing from "@patternfly/patternfly/utilities/Sizing/sizing.css";
|
||||
@customElement("ak-group-view")
|
||||
export class GroupViewPage extends AKElement {
|
||||
@property({ type: String })
|
||||
set groupId(id: string) {
|
||||
public set groupId(id: string) {
|
||||
new CoreApi(DEFAULT_CONFIG)
|
||||
.coreGroupsRetrieve({
|
||||
groupUuid: id,
|
||||
@@ -47,9 +47,9 @@ export class GroupViewPage extends AKElement {
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
group?: Group;
|
||||
public group?: Group;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFPage,
|
||||
PFButton,
|
||||
@@ -62,7 +62,7 @@ export class GroupViewPage extends AKElement {
|
||||
PFSizing,
|
||||
];
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
this.addEventListener(EVENT_REFRESH, () => {
|
||||
if (!this.group?.pk) return;
|
||||
@@ -70,7 +70,7 @@ export class GroupViewPage extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
return html`<ak-page-header
|
||||
icon="pf-icon pf-icon-users"
|
||||
header=${msg(str`Group ${this.group?.name || ""}`)}
|
||||
@@ -80,7 +80,7 @@ export class GroupViewPage extends AKElement {
|
||||
${this.renderBody()}`;
|
||||
}
|
||||
|
||||
renderBody(): TemplateResult {
|
||||
protected renderBody(): TemplateResult {
|
||||
if (!this.group) {
|
||||
return html``;
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ type UserListRequestFilter = Partial<Pick<CoreUsersListRequest, "isActive">>;
|
||||
|
||||
@customElement("ak-group-member-select-table")
|
||||
export class MemberSelectTable extends TableModal<User> {
|
||||
static styles = [
|
||||
public static override styles = [
|
||||
...super.styles,
|
||||
css`
|
||||
.show-disabled-toggle-group {
|
||||
@@ -31,24 +31,24 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
`,
|
||||
];
|
||||
|
||||
checkbox = true;
|
||||
checkboxChip = true;
|
||||
public override checkbox = true;
|
||||
public override checkboxChip = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
confirm!: (selectedItems: User[]) => Promise<unknown>;
|
||||
public confirm!: (selectedItems: User[]) => Promise<unknown>;
|
||||
|
||||
userListFilter: UserListFilter = "active";
|
||||
protected userListFilter: UserListFilter = "active";
|
||||
|
||||
order = "username";
|
||||
public override order = "username";
|
||||
|
||||
// The `userListRequestFilter` clause is necessary because the back-end for searches is
|
||||
// tri-state: `isActive: true` will only show active users, `isActive: false` will show only
|
||||
// inactive users; only when it's _missing_ will you get all users.
|
||||
async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
const userListRequestFilter: UserListRequestFilter = match(this.userListFilter)
|
||||
.with("all", () => ({}))
|
||||
.with("active", () => ({ isActive: true }))
|
||||
@@ -61,7 +61,7 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "username"),
|
||||
new TableColumn(msg("Active"), "is_active"),
|
||||
@@ -69,7 +69,7 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarAfter() {
|
||||
protected override renderToolbarAfter() {
|
||||
const toggleShowDisabledUsers = () => {
|
||||
this.userListFilter = this.userListFilter === "all" ? "active" : "all";
|
||||
this.page = 1;
|
||||
@@ -99,7 +99,7 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
</div>`;
|
||||
}
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
protected row(item: User): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.username}</div>
|
||||
<small>${item.name}</small>`,
|
||||
@@ -111,11 +111,11 @@ export class MemberSelectTable extends TableModal<User> {
|
||||
];
|
||||
}
|
||||
|
||||
renderSelectedChip(item: User): TemplateResult {
|
||||
protected override renderSelectedChip(item: User): TemplateResult {
|
||||
return html`${item.username}`;
|
||||
}
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
protected override renderModalInner(): TemplateResult {
|
||||
return html`<section class="pf-c-modal-box__header pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1 class="pf-c-title pf-m-2xl">${msg("Select users to add")}</h1>
|
||||
|
||||
@@ -22,16 +22,16 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@customElement("ak-group-related-add")
|
||||
export class RelatedGroupAdd extends Form<{ groups: string[] }> {
|
||||
@property({ attribute: false })
|
||||
user?: User;
|
||||
public user?: User;
|
||||
|
||||
@state()
|
||||
groupsToAdd: Group[] = [];
|
||||
protected groupsToAdd: Group[] = [];
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully added user to group(s).");
|
||||
}
|
||||
|
||||
async send(data: { groups: string[] }): Promise<unknown> {
|
||||
protected async send(data: { groups: string[] }): Promise<unknown> {
|
||||
await Promise.all(
|
||||
data.groups.map((group) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsAddUserCreate({
|
||||
@@ -45,7 +45,7 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
|
||||
return data;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html`<ak-form-element-horizontal label=${msg("Groups to add")} name="groups">
|
||||
<div class="pf-c-input-group">
|
||||
<ak-user-group-select-table
|
||||
@@ -85,19 +85,20 @@ export class RelatedGroupAdd extends Form<{ groups: string[] }> {
|
||||
|
||||
@customElement("ak-group-related-list")
|
||||
export class RelatedGroupList extends Table<Group> {
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
searchEnabled(): boolean {
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
@property({ attribute: false })
|
||||
targetUser?: User;
|
||||
public targetUser?: User;
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Group>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Group>> {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
membersByPk: this.targetUser ? [this.targetUser.pk] : [],
|
||||
@@ -105,7 +106,7 @@ export class RelatedGroupList extends Table<Group> {
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Parent"), "parent"),
|
||||
@@ -114,7 +115,7 @@ export class RelatedGroupList extends Table<Group> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Group(s)")}
|
||||
@@ -140,7 +141,7 @@ export class RelatedGroupList extends Table<Group> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: Group): TemplateResult[] {
|
||||
protected row(item: Group): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/identity/groups/${item.pk}">${item.name}</a>`,
|
||||
html`${item.parentName || msg("-")}`,
|
||||
@@ -158,7 +159,7 @@ export class RelatedGroupList extends Table<Group> {
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
${this.targetUser
|
||||
? html`<ak-forms-modal>
|
||||
|
||||
@@ -41,16 +41,16 @@ import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList
|
||||
@customElement("ak-user-related-add")
|
||||
export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
@property({ attribute: false })
|
||||
group?: Group;
|
||||
public group?: Group;
|
||||
|
||||
@state()
|
||||
usersToAdd: User[] = [];
|
||||
protected usersToAdd: User[] = [];
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return msg("Successfully added user(s).");
|
||||
}
|
||||
|
||||
async send(data: { users: number[] }): Promise<{ users: number[] }> {
|
||||
protected async send(data: { users: number[] }): Promise<{ users: number[] }> {
|
||||
await Promise.all(
|
||||
data.users.map((user) => {
|
||||
return new CoreApi(DEFAULT_CONFIG).coreGroupsAddUserCreate({
|
||||
@@ -64,7 +64,7 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
return data;
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Users to add")} name="users">
|
||||
<div class="pf-c-input-group">
|
||||
<ak-group-member-select-table
|
||||
@@ -104,29 +104,29 @@ export class RelatedUserAdd extends Form<{ users: number[] }> {
|
||||
|
||||
@customElement("ak-user-related-list")
|
||||
export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Table<User>)) {
|
||||
expandable = true;
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override expandable = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
searchEnabled(): boolean {
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property({ attribute: false })
|
||||
targetGroup?: Group;
|
||||
public targetGroup?: Group;
|
||||
|
||||
@property()
|
||||
order = "last_login";
|
||||
public override order = "last_login";
|
||||
|
||||
@property({ type: Boolean })
|
||||
hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true);
|
||||
public hideServiceAccounts = getURLParam<boolean>("hideServiceAccounts", true);
|
||||
|
||||
@state()
|
||||
me?: SessionUser;
|
||||
protected me?: SessionUser;
|
||||
|
||||
static styles: CSSResult[] = [...Table.styles, PFDescriptionList, PFAlert, PFBanner];
|
||||
public static styles: CSSResult[] = [...Table.styles, PFDescriptionList, PFAlert, PFBanner];
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<User>> {
|
||||
const users = await new CoreApi(DEFAULT_CONFIG).coreUsersList({
|
||||
...(await this.defaultEndpointConfig()),
|
||||
groupsByPk: this.targetGroup ? [this.targetGroup.pk] : [],
|
||||
@@ -139,7 +139,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
return users;
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "username"),
|
||||
new TableColumn(msg("Active"), "is_active"),
|
||||
@@ -148,7 +148,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("User(s)")}
|
||||
@@ -180,7 +180,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
protected row(item: User): TemplateResult[] {
|
||||
const canImpersonate =
|
||||
this.can(CapabilitiesEnum.CanImpersonate) && item.pk !== this.me?.user.pk;
|
||||
return [
|
||||
@@ -226,7 +226,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: User): TemplateResult {
|
||||
protected override renderExpanded(item: User): TemplateResult {
|
||||
return html`<td role="cell" colspan="3">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
<dl class="pf-c-description-list pf-m-horizontal">
|
||||
@@ -374,7 +374,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
<td></td>`;
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
protected override renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
${this.targetGroup
|
||||
? html`<ak-forms-modal>
|
||||
@@ -450,7 +450,7 @@ export class RelatedUserList extends WithBrandConfig(WithCapabilitiesConfig(Tabl
|
||||
`;
|
||||
}
|
||||
|
||||
renderToolbarAfter(): TemplateResult {
|
||||
protected override renderToolbarAfter(): TemplateResult {
|
||||
return html`
|
||||
<div class="pf-c-toolbar__group pf-m-filter-group">
|
||||
<div class="pf-c-toolbar__item pf-m-search-filter">
|
||||
|
||||
@@ -14,9 +14,9 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
@customElement("ak-outpost-deployment-modal")
|
||||
export class OutpostDeploymentModal extends ModalButton {
|
||||
@property({ attribute: false })
|
||||
outpost?: Outpost;
|
||||
public outpost?: Outpost;
|
||||
|
||||
renderModalInner(): TemplateResult {
|
||||
protected override renderModalInner(): TemplateResult {
|
||||
return html`<div class="pf-c-modal-box__header">
|
||||
<h1 class="pf-c-title pf-m-2xl">${msg("Outpost Deployment Info")}</h1>
|
||||
</div>
|
||||
|
||||
@@ -94,17 +94,17 @@ function providerProvider(type: OutpostTypeEnum): DataProvider {
|
||||
@customElement("ak-outpost-form")
|
||||
export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
@property()
|
||||
type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
|
||||
public type: OutpostTypeEnum = OutpostTypeEnum.Proxy;
|
||||
|
||||
@property({ type: Boolean })
|
||||
embedded = false;
|
||||
public embedded = false;
|
||||
|
||||
@state()
|
||||
providers: DataProvider = providerProvider(this.type);
|
||||
protected providers: DataProvider = providerProvider(this.type);
|
||||
|
||||
defaultConfig?: OutpostDefaultConfig;
|
||||
protected defaultConfig?: OutpostDefaultConfig;
|
||||
|
||||
async loadInstance(pk: string): Promise<Outpost> {
|
||||
protected async loadInstance(pk: string): Promise<Outpost> {
|
||||
const o = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesRetrieve({
|
||||
uuid: pk,
|
||||
});
|
||||
@@ -113,20 +113,20 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
return o;
|
||||
}
|
||||
|
||||
async load(): Promise<void> {
|
||||
public override async load(): Promise<void> {
|
||||
this.defaultConfig = await new OutpostsApi(
|
||||
DEFAULT_CONFIG,
|
||||
).outpostsInstancesDefaultSettingsRetrieve();
|
||||
this.providers = providerProvider(this.type);
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated outpost.")
|
||||
: msg("Successfully created outpost.");
|
||||
}
|
||||
|
||||
async send(data: Outpost): Promise<Outpost> {
|
||||
protected async send(data: Outpost): Promise<Outpost> {
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
@@ -138,7 +138,7 @@ export class OutpostForm extends ModelForm<Outpost, string> {
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
const typeOptions = [
|
||||
[OutpostTypeEnum.Proxy, msg("Proxy")],
|
||||
[OutpostTypeEnum.Ldap, msg("LDAP")],
|
||||
|
||||
@@ -17,9 +17,9 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-outpost-health")
|
||||
export class OutpostHealthElement extends AKElement {
|
||||
@property({ attribute: false })
|
||||
outpostHealth?: OutpostHealth;
|
||||
public outpostHealth?: OutpostHealth;
|
||||
|
||||
static styles: CSSResult[] = [
|
||||
public static override styles: CSSResult[] = [
|
||||
PFBase,
|
||||
PFDescriptionList,
|
||||
css`
|
||||
@@ -29,7 +29,7 @@ export class OutpostHealthElement extends AKElement {
|
||||
`,
|
||||
];
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
if (!this.outpostHealth) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
|
||||
@@ -18,20 +18,20 @@ import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
@customElement("ak-outpost-health-simple")
|
||||
export class OutpostHealthSimpleElement extends AKElement {
|
||||
@property()
|
||||
outpostId?: string;
|
||||
public outpostId?: string;
|
||||
|
||||
@state()
|
||||
outpostHealths: OutpostHealth[] = [];
|
||||
protected outpostHealths: OutpostHealth[] = [];
|
||||
|
||||
@property({ attribute: false })
|
||||
loaded = false;
|
||||
public loaded = false;
|
||||
|
||||
@property({ attribute: false })
|
||||
showVersion = true;
|
||||
public showVersion = true;
|
||||
|
||||
static styles: CSSResult[] = [PFBase];
|
||||
public static override styles: CSSResult[] = [PFBase];
|
||||
|
||||
constructor() {
|
||||
public constructor() {
|
||||
super();
|
||||
window.addEventListener(EVENT_REFRESH, () => {
|
||||
this.outpostHealths = [];
|
||||
@@ -39,7 +39,7 @@ export class OutpostHealthSimpleElement extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
firstUpdated(): void {
|
||||
public override firstUpdated(): void {
|
||||
if (!this.outpostId) return;
|
||||
new OutpostsApi(DEFAULT_CONFIG)
|
||||
.outpostsInstancesHealthList({
|
||||
@@ -51,7 +51,7 @@ export class OutpostHealthSimpleElement extends AKElement {
|
||||
});
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
public override render(): TemplateResult {
|
||||
if (!this.outpostId || !this.loaded) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
|
||||
@@ -51,24 +51,27 @@ export function TypeToLabel(type?: OutpostTypeEnum): string {
|
||||
|
||||
@customElement("ak-outpost-list")
|
||||
export class OutpostListPage extends TablePage<Outpost> {
|
||||
expandable = true;
|
||||
public override expandable = true;
|
||||
|
||||
pageTitle(): string {
|
||||
protected pageTitle(): string {
|
||||
return msg("Outposts");
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
|
||||
protected pageDescription(): string | undefined {
|
||||
return msg(
|
||||
"Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-zone";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<Outpost>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<Outpost>> {
|
||||
const outposts = await new OutpostsApi(DEFAULT_CONFIG).outpostsInstancesList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
@@ -87,9 +90,9 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
}
|
||||
|
||||
@state()
|
||||
health: { [key: string]: OutpostHealth[] } = {};
|
||||
protected health: { [key: string]: OutpostHealth[] } = {};
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Type"), "type"),
|
||||
@@ -100,15 +103,15 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
];
|
||||
}
|
||||
|
||||
static styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
public static override styles: CSSResult[] = [...super.styles, PFDescriptionList];
|
||||
|
||||
checkbox = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
row(item: Outpost): TemplateResult[] {
|
||||
protected row(item: Outpost): TemplateResult[] {
|
||||
return [
|
||||
html`<div>${item.name}</div>
|
||||
${item.config.authentik_host === ""
|
||||
@@ -162,7 +165,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: Outpost): TemplateResult {
|
||||
protected override renderExpanded(item: Outpost): TemplateResult {
|
||||
const [appLabel, modelName] = ModelEnum.AuthentikOutpostsOutpost.split(".");
|
||||
return html`<td role="cell" colspan="7">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -218,7 +221,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Outpost(s)")}
|
||||
@@ -240,7 +243,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`
|
||||
<ak-forms-modal>
|
||||
<span slot="submit"> ${msg("Create")} </span>
|
||||
|
||||
@@ -15,19 +15,19 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-service-connection-docker-form")
|
||||
export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnection, string> {
|
||||
loadInstance(pk: string): Promise<DockerServiceConnection> {
|
||||
protected loadInstance(pk: string): Promise<DockerServiceConnection> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerRetrieve({
|
||||
uuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated integration.")
|
||||
: msg("Successfully created integration.");
|
||||
}
|
||||
|
||||
async send(data: DockerServiceConnection): Promise<DockerServiceConnection> {
|
||||
protected async send(data: DockerServiceConnection): Promise<DockerServiceConnection> {
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsDockerUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
@@ -39,7 +39,7 @@ export class ServiceConnectionDockerForm extends ModelForm<DockerServiceConnecti
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -20,19 +20,19 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
|
||||
KubernetesServiceConnection,
|
||||
string
|
||||
> {
|
||||
loadInstance(pk: string): Promise<KubernetesServiceConnection> {
|
||||
protected loadInstance(pk: string): Promise<KubernetesServiceConnection> {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesRetrieve({
|
||||
uuid: pk,
|
||||
});
|
||||
}
|
||||
|
||||
getSuccessMessage(): string {
|
||||
public override getSuccessMessage(): string {
|
||||
return this.instance
|
||||
? msg("Successfully updated integration.")
|
||||
: msg("Successfully created integration.");
|
||||
}
|
||||
|
||||
async send(data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> {
|
||||
protected async send(data: KubernetesServiceConnection): Promise<KubernetesServiceConnection> {
|
||||
if (this.instance) {
|
||||
return new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsKubernetesUpdate({
|
||||
uuid: this.instance.pk || "",
|
||||
@@ -44,7 +44,7 @@ export class ServiceConnectionKubernetesForm extends ModelForm<
|
||||
});
|
||||
}
|
||||
|
||||
renderForm(): TemplateResult {
|
||||
protected override renderForm(): TemplateResult {
|
||||
return html` <ak-form-element-horizontal label=${msg("Name")} required name="name">
|
||||
<input
|
||||
type="text"
|
||||
|
||||
@@ -27,26 +27,29 @@ import { ifDefined } from "lit/directives/if-defined.js";
|
||||
|
||||
@customElement("ak-outpost-service-connection-list")
|
||||
export class OutpostServiceConnectionListPage extends TablePage<ServiceConnection> {
|
||||
pageTitle(): string {
|
||||
protected pageTitle(): string {
|
||||
return msg("Outpost integrations");
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
|
||||
protected pageDescription(): string | undefined {
|
||||
return msg(
|
||||
"Outpost integrations define how authentik connects to external platforms to manage and deploy Outposts.",
|
||||
);
|
||||
}
|
||||
pageIcon(): string {
|
||||
|
||||
protected pageIcon(): string {
|
||||
return "pf-icon pf-icon-integration";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
|
||||
protected override searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
checkbox = true;
|
||||
expandable = true;
|
||||
clearOnRefresh = true;
|
||||
public override checkbox = true;
|
||||
public override expandable = true;
|
||||
public override clearOnRefresh = true;
|
||||
|
||||
async apiEndpoint(): Promise<PaginatedResponse<ServiceConnection>> {
|
||||
protected async apiEndpoint(): Promise<PaginatedResponse<ServiceConnection>> {
|
||||
const connections = await new OutpostsApi(DEFAULT_CONFIG).outpostsServiceConnectionsAllList(
|
||||
await this.defaultEndpointConfig(),
|
||||
);
|
||||
@@ -65,9 +68,9 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
}
|
||||
|
||||
@state()
|
||||
state: { [key: string]: ServiceConnectionState } = {};
|
||||
protected state: { [key: string]: ServiceConnectionState } = {};
|
||||
|
||||
columns(): TableColumn[] {
|
||||
protected columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn(msg("Name"), "name"),
|
||||
new TableColumn(msg("Type")),
|
||||
@@ -78,9 +81,9 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
public override order = "name";
|
||||
|
||||
row(item: ServiceConnection): TemplateResult[] {
|
||||
protected row(item: ServiceConnection): TemplateResult[] {
|
||||
const itemState = this.state[item.pk];
|
||||
return [
|
||||
html`${item.name}`,
|
||||
@@ -113,7 +116,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
];
|
||||
}
|
||||
|
||||
renderExpanded(item: ServiceConnection): TemplateResult {
|
||||
protected override renderExpanded(item: ServiceConnection): TemplateResult {
|
||||
const [appLabel, modelName] = item.metaModelName.split(".");
|
||||
return html` <td role="cell" colspan="5">
|
||||
<div class="pf-c-table__expandable-row-content">
|
||||
@@ -153,7 +156,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
</td>`;
|
||||
}
|
||||
|
||||
renderToolbarSelected(): TemplateResult {
|
||||
protected override renderToolbarSelected(): TemplateResult {
|
||||
const disabled = this.selectedElements.length < 1;
|
||||
return html`<ak-forms-delete-bulk
|
||||
objectLabel=${msg("Outpost integration(s)")}
|
||||
@@ -175,7 +178,7 @@ export class OutpostServiceConnectionListPage extends TablePage<ServiceConnectio
|
||||
</ak-forms-delete-bulk>`;
|
||||
}
|
||||
|
||||
renderObjectCreate(): TemplateResult {
|
||||
protected override renderObjectCreate(): TemplateResult {
|
||||
return html`<ak-service-connection-wizard></ak-service-connection-wizard> `;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user