Merge branch 'master' into stage-challenge
# Conflicts: # authentik/api/v2/urls.py
This commit is contained in:
@ -1,4 +1,4 @@
|
||||
import { DefaultClient, AKResponse, QueryArguments } from "./Client";
|
||||
import { DefaultClient, AKResponse, QueryArguments, BaseInheritanceModel } from "./Client";
|
||||
import { TypeCreate } from "./Providers";
|
||||
|
||||
export enum FlowDesignation {
|
||||
@ -40,8 +40,8 @@ export class Flow {
|
||||
}
|
||||
|
||||
static cached(): Promise<number> {
|
||||
return DefaultClient.fetch<AKResponse<Flow>>(["flows", "cached"]).then(r => {
|
||||
return r.pagination.count;
|
||||
return DefaultClient.fetch<{ count: number }>(["flows", "all", "cached"]).then(r => {
|
||||
return r.count;
|
||||
});
|
||||
}
|
||||
static adminUrl(rest: string): string {
|
||||
@ -49,16 +49,26 @@ export class Flow {
|
||||
}
|
||||
}
|
||||
|
||||
export class Stage {
|
||||
export class Stage implements BaseInheritanceModel {
|
||||
pk: string;
|
||||
name: string;
|
||||
__type__: string;
|
||||
object_type: string;
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
flow_set: Flow[];
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(slug: string): Promise<Stage> {
|
||||
return DefaultClient.fetch<Stage>(["stages", "all", slug]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<Stage>> {
|
||||
return DefaultClient.fetch<AKResponse<Stage>>(["stages", "all"], filter);
|
||||
}
|
||||
|
||||
static getTypes(): Promise<TypeCreate[]> {
|
||||
return DefaultClient.fetch<TypeCreate[]>(["stages", "all", "types"]);
|
||||
}
|
||||
|
@ -1,15 +1,28 @@
|
||||
import { DefaultClient, QueryArguments, AKResponse } from "./Client";
|
||||
import { EventContext } from "./Events";
|
||||
|
||||
export class Group {
|
||||
|
||||
group_uuid: string;
|
||||
pk: string;
|
||||
name: string;
|
||||
is_superuser: boolean;
|
||||
attributes: EventContext;
|
||||
parent?: Group;
|
||||
users: number[];
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<Group> {
|
||||
return DefaultClient.fetch<Group>(["core", "groups", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<Group>> {
|
||||
return DefaultClient.fetch<AKResponse<Group>>(["core", "groups"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/groups/${rest}`;
|
||||
}
|
||||
}
|
||||
|
27
web/src/api/Invitations.ts
Normal file
27
web/src/api/Invitations.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { DefaultClient, QueryArguments, AKResponse } from "./Client";
|
||||
import { EventContext } from "./Events";
|
||||
import { User } from "./Users";
|
||||
|
||||
export class Invitation {
|
||||
|
||||
pk: string;
|
||||
expires: number;
|
||||
fixed_date: EventContext;
|
||||
created_by: User;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<Invitation> {
|
||||
return DefaultClient.fetch<Invitation>(["stages", "invitation", "invitations", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<Invitation>> {
|
||||
return DefaultClient.fetch<AKResponse<Invitation>>(["stages", "invitation", "invitations"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/stages/invitations/${rest}`;
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
import { DefaultClient, AKResponse, QueryArguments } from "./Client";
|
||||
import { Provider } from "./Providers";
|
||||
import { Provider, TypeCreate } from "./Providers";
|
||||
|
||||
export interface OutpostHealth {
|
||||
last_seen: number;
|
||||
@ -38,3 +38,42 @@ export class Outpost {
|
||||
return `/administration/outposts/${rest}`;
|
||||
}
|
||||
}
|
||||
|
||||
export interface OutpostServiceConnectionState {
|
||||
version: string;
|
||||
healthy: boolean;
|
||||
}
|
||||
|
||||
export class OutpostServiceConnection {
|
||||
pk: string;
|
||||
name: string;
|
||||
local: boolean;
|
||||
object_type: string;
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<OutpostServiceConnection> {
|
||||
return DefaultClient.fetch<OutpostServiceConnection>(["outposts", "service_connections", "all", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<OutpostServiceConnection>> {
|
||||
return DefaultClient.fetch<AKResponse<OutpostServiceConnection>>(["outposts", "service_connections", "all"], filter);
|
||||
}
|
||||
|
||||
static state(pk: string): Promise<OutpostServiceConnectionState> {
|
||||
return DefaultClient.fetch<OutpostServiceConnectionState>(["outposts", "service_connections", "all", pk, "state"]);
|
||||
}
|
||||
|
||||
static getTypes(): Promise<TypeCreate[]> {
|
||||
return DefaultClient.fetch<TypeCreate[]>(["outposts", "service_connections", "all", "types"]);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/outpost_service_connections/${rest}`;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,15 +1,18 @@
|
||||
import { DefaultClient, BaseInheritanceModel, AKResponse, QueryArguments } from "./Client";
|
||||
import { TypeCreate } from "./Providers";
|
||||
|
||||
export class Policy implements BaseInheritanceModel {
|
||||
pk: string;
|
||||
name: string;
|
||||
execution_logging: boolean;
|
||||
object_type: string;
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
bound_to: number;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
object_type: string;
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
|
||||
static get(pk: string): Promise<Policy> {
|
||||
return DefaultClient.fetch<Policy>(["policies", "all", pk]);
|
||||
@ -20,8 +23,16 @@ export class Policy implements BaseInheritanceModel {
|
||||
}
|
||||
|
||||
static cached(): Promise<number> {
|
||||
return DefaultClient.fetch<AKResponse<Policy>>(["policies", "cached"]).then(r => {
|
||||
return r.pagination.count;
|
||||
return DefaultClient.fetch<{ count: number }>(["policies", "all", "cached"]).then(r => {
|
||||
return r.count;
|
||||
});
|
||||
}
|
||||
|
||||
static getTypes(): Promise<TypeCreate[]> {
|
||||
return DefaultClient.fetch<TypeCreate[]>(["policies", "all", "types"]);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/policies/${rest}`;
|
||||
}
|
||||
}
|
||||
|
30
web/src/api/Prompts.ts
Normal file
30
web/src/api/Prompts.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import { DefaultClient, QueryArguments, AKResponse } from "./Client";
|
||||
import { Stage } from "./Flows";
|
||||
|
||||
export class Prompt {
|
||||
|
||||
pk: string;
|
||||
field_key: string;
|
||||
label: string;
|
||||
type: string;
|
||||
required: boolean;
|
||||
placeholder: string;
|
||||
order: number;
|
||||
promptstage_set: Stage[];
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<Prompt> {
|
||||
return DefaultClient.fetch<Prompt>(["stages", "prompt", "prompts", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<Prompt>> {
|
||||
return DefaultClient.fetch<AKResponse<Prompt>>(["stages", "prompt", "prompts"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/stages/prompts/${rest}`;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
import { DefaultClient, AKResponse, QueryArguments } from "./Client";
|
||||
import { TypeCreate } from "./Providers";
|
||||
|
||||
export class PropertyMapping {
|
||||
pk: string;
|
||||
@ -20,6 +21,10 @@ export class PropertyMapping {
|
||||
return DefaultClient.fetch<AKResponse<PropertyMapping>>(["propertymappings", "all"], filter);
|
||||
}
|
||||
|
||||
static getTypes(): Promise<TypeCreate[]> {
|
||||
return DefaultClient.fetch<TypeCreate[]>(["propertymappings", "all", "types"]);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/property-mappings/${rest}`;
|
||||
}
|
||||
|
33
web/src/api/SystemTask.ts
Normal file
33
web/src/api/SystemTask.ts
Normal file
@ -0,0 +1,33 @@
|
||||
import { DefaultClient, QueryArguments } from "./Client";
|
||||
|
||||
export enum TaskStatus {
|
||||
SUCCESSFUL = 1,
|
||||
WARNING = 2,
|
||||
ERROR = 4,
|
||||
}
|
||||
|
||||
export class SystemTask {
|
||||
|
||||
task_name: string;
|
||||
task_description: string;
|
||||
task_finish_timestamp: number;
|
||||
status: TaskStatus;
|
||||
messages: string[];
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(task_name: string): Promise<SystemTask> {
|
||||
return DefaultClient.fetch<SystemTask>(["admin", "system_tasks", task_name]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<SystemTask[]> {
|
||||
return DefaultClient.fetch<SystemTask[]>(["admin", "system_tasks"], filter);
|
||||
}
|
||||
|
||||
static retry(task_name: string): string {
|
||||
return DefaultClient.makeUrl(["admin", "system_tasks", task_name, "retry"]);
|
||||
}
|
||||
|
||||
}
|
@ -1,11 +1,47 @@
|
||||
import { DefaultClient } from "./Client";
|
||||
import { AKResponse, DefaultClient, QueryArguments } from "./Client";
|
||||
import { User } from "./Users";
|
||||
|
||||
interface TokenResponse {
|
||||
key: string;
|
||||
export enum TokenIntent {
|
||||
INTENT_VERIFICATION = "verification",
|
||||
INTENT_API = "api",
|
||||
INTENT_RECOVERY = "recovery",
|
||||
}
|
||||
|
||||
export function tokenByIdentifier(identifier: string): Promise<string> {
|
||||
return DefaultClient.fetch<TokenResponse>(["core", "tokens", identifier, "view_key"]).then(
|
||||
(r) => r.key
|
||||
);
|
||||
export class Token {
|
||||
|
||||
pk: string;
|
||||
identifier: string;
|
||||
intent: TokenIntent;
|
||||
user: User;
|
||||
description: string;
|
||||
|
||||
expires: number;
|
||||
expiring: boolean;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<User> {
|
||||
return DefaultClient.fetch<User>(["core", "tokens", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<Token>> {
|
||||
return DefaultClient.fetch<AKResponse<Token>>(["core", "tokens"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/tokens/${rest}`;
|
||||
}
|
||||
|
||||
static userUrl(rest: string): string {
|
||||
return `/-/user/tokens/${rest}`;
|
||||
}
|
||||
|
||||
static getKey(identifier: string): Promise<string> {
|
||||
return DefaultClient.fetch<{ key: string }>(["core", "tokens", identifier, "view_key"]).then(
|
||||
(r) => r.key
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { DefaultClient, AKResponse } from "./Client";
|
||||
import { DefaultClient, AKResponse, QueryArguments } from "./Client";
|
||||
|
||||
let _globalMePromise: Promise<User>;
|
||||
|
||||
@ -9,11 +9,25 @@ export class User {
|
||||
is_superuser: boolean;
|
||||
email: boolean;
|
||||
avatar: string;
|
||||
is_active: boolean;
|
||||
last_login: number;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<User> {
|
||||
return DefaultClient.fetch<User>(["core", "users", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<AKResponse<User>> {
|
||||
return DefaultClient.fetch<AKResponse<User>>(["core", "users"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/users/${rest}`;
|
||||
}
|
||||
|
||||
static me(): Promise<User> {
|
||||
if (!_globalMePromise) {
|
||||
_globalMePromise = DefaultClient.fetch<User>(["core", "users", "me"]);
|
||||
|
@ -85,10 +85,6 @@ select[multiple] {
|
||||
z-index: auto !important;
|
||||
}
|
||||
|
||||
.pf-c-page__main {
|
||||
display: block;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
:root {
|
||||
--ak-dark-foreground: #fafafa;
|
||||
|
@ -70,18 +70,18 @@ export class SpinnerButton extends LitElement {
|
||||
@click=${() => this.callAction()}
|
||||
>
|
||||
${this.isRunning
|
||||
? html` <span class="pf-c-button__progress">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-md"
|
||||
role="progressbar"
|
||||
aria-valuetext="Loading..."
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</span>`
|
||||
: ""}
|
||||
? html` <span class="pf-c-button__progress">
|
||||
<span
|
||||
class="pf-c-spinner pf-m-md"
|
||||
role="progressbar"
|
||||
aria-valuetext="Loading..."
|
||||
>
|
||||
<span class="pf-c-spinner__clipper"></span>
|
||||
<span class="pf-c-spinner__lead-ball"></span>
|
||||
<span class="pf-c-spinner__tail-ball"></span>
|
||||
</span>
|
||||
</span>`
|
||||
: ""}
|
||||
<slot></slot>
|
||||
</button>`;
|
||||
}
|
||||
|
@ -3,7 +3,7 @@ import { css, CSSResult, customElement, html, LitElement, property, TemplateResu
|
||||
import GlobalsStyle from "@patternfly/patternfly/base/patternfly-globals.css";
|
||||
// @ts-ignore
|
||||
import ButtonStyle from "@patternfly/patternfly/components/Button/button.css";
|
||||
import { tokenByIdentifier } from "../../api/Tokens";
|
||||
import { Token } from "../../api/Tokens";
|
||||
import { ColorStyles, ERROR_CLASS, PRIMARY_CLASS, SUCCESS_CLASS } from "../../constants";
|
||||
|
||||
@customElement("ak-token-copy-button")
|
||||
@ -35,7 +35,7 @@ export class TokenCopyButton extends LitElement {
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
tokenByIdentifier(this.identifier).then((token) => {
|
||||
Token.getKey(this.identifier).then((token) => {
|
||||
navigator.clipboard.writeText(token).then(() => {
|
||||
this.buttonClass = SUCCESS_CLASS;
|
||||
setTimeout(() => {
|
||||
|
@ -47,6 +47,7 @@ export class MessageContainer extends LitElement {
|
||||
this.messageSocket = new WebSocket(wsUrl);
|
||||
this.messageSocket.addEventListener("open", () => {
|
||||
console.debug(`authentik/messages: connected to ${wsUrl}`);
|
||||
this.retryDelay = 200;
|
||||
});
|
||||
this.messageSocket.addEventListener("close", (e) => {
|
||||
console.debug(`authentik/messages: closed ws connection: ${e}`);
|
||||
|
@ -1,8 +1,13 @@
|
||||
import { customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import { CSSResult, customElement, html, LitElement, TemplateResult } from "lit-element";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
@customElement("ak-notification-trigger")
|
||||
export class NotificationRule extends LitElement {
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener("click", () => {
|
||||
@ -16,7 +21,8 @@ export class NotificationRule extends LitElement {
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<slot></slot>`;
|
||||
// TODO: Show icon with red dot when unread notifications exist
|
||||
return html`<i class="fas fa-bell pf-c-dropdown__toggle-icon" aria-hidden="true"></i>`;
|
||||
}
|
||||
|
||||
}
|
||||
|
27
web/src/elements/router/Router404.ts
Normal file
27
web/src/elements/router/Router404.ts
Normal file
@ -0,0 +1,27 @@
|
||||
import { gettext } from "django";
|
||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
@customElement("ak-router-404")
|
||||
export class Router404 extends LitElement {
|
||||
|
||||
@property()
|
||||
url = "";
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html`<div class="pf-c-empty-state pf-m-full-height">
|
||||
<div class="pf-c-empty-state__content">
|
||||
<i class="fas fa-question-circle pf-c-empty-state__icon" aria-hidden="true"></i>
|
||||
<h1 class="pf-c-title pf-m-lg">${gettext("Not found")}</h1>
|
||||
<div class="pf-c-empty-state__body">
|
||||
${gettext(`The url '${this.url}' was not found.`)}
|
||||
</div>
|
||||
<a href="#/" class="pf-c-button pf-m-primary" type="button">${gettext("Return home")}</a>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
}
|
@ -10,6 +10,7 @@ import { ROUTES } from "../../routes";
|
||||
import { RouteMatch } from "./RouteMatch";
|
||||
|
||||
import "../../pages/generic/SiteShell";
|
||||
import "./Router404";
|
||||
|
||||
@customElement("ak-router-outlet")
|
||||
export class RouterOutlet extends LitElement {
|
||||
@ -28,6 +29,11 @@ export class RouterOutlet extends LitElement {
|
||||
:host {
|
||||
height: 100vh;
|
||||
}
|
||||
*:first-child {
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
`,
|
||||
].concat(...COMMON_STYLES);
|
||||
}
|
||||
@ -62,12 +68,12 @@ export class RouterOutlet extends LitElement {
|
||||
}
|
||||
});
|
||||
if (!matchedRoute) {
|
||||
console.debug(`authentik/router: route "${activeUrl}" not defined, defaulting to shell`);
|
||||
console.debug(`authentik/router: route "${activeUrl}" not defined`);
|
||||
const route = new Route(
|
||||
RegExp(""),
|
||||
html`<ak-site-shell url=${activeUrl}>
|
||||
<div slot="body"></div>
|
||||
</ak-site-shell>`
|
||||
html`<div class="pf-c-page__main">
|
||||
<ak-router-404 url=${activeUrl}></ak-router-404>
|
||||
</div>`
|
||||
);
|
||||
matchedRoute = new RouteMatch(route);
|
||||
matchedRoute.arguments = route.url.exec(activeUrl)?.groups || {};
|
||||
@ -77,7 +83,6 @@ export class RouterOutlet extends LitElement {
|
||||
}
|
||||
|
||||
render(): TemplateResult | undefined {
|
||||
// TODO: Render 404 when current Route is empty
|
||||
return this.current?.render();
|
||||
}
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ export class SidebarItem {
|
||||
this.condition = async () => true;
|
||||
this.activeMatchers = [];
|
||||
if (this.path) {
|
||||
this.activeMatchers.push(new RegExp(`^${this.path}`));
|
||||
this.activeMatchers.push(new RegExp(`^${this.path}$`));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@ export class SidebarUser extends LitElement {
|
||||
render(): TemplateResult {
|
||||
return html`
|
||||
<a href="#/-/user/" class="pf-c-nav__link user-avatar" id="user-settings">
|
||||
${until(User.me().then(u => {
|
||||
return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`;}), html``)}
|
||||
${until(User.me().then((u) => {
|
||||
return html`<img class="pf-c-avatar" src="${u.avatar}" alt="" />`;
|
||||
}), html``)}
|
||||
</a>
|
||||
<ak-notification-trigger class="pf-c-nav__link user-notifications">
|
||||
<i class="fas fa-bell pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</ak-notification-trigger>
|
||||
<a href="/flows/-/default/invalidation/" class="pf-c-nav__link user-logout" id="logout">
|
||||
<i class="fas fa-sign-out-alt" aria-hidden="true"></i>
|
||||
|
@ -43,7 +43,7 @@ export class TablePagination extends LitElement {
|
||||
<button
|
||||
class="pf-c-button pf-m-plain"
|
||||
@click=${() => { this.pageChangeHandler(this.pages?.next || 0); }}
|
||||
?disabled="${(this.pages?.next || 0) < 0}"
|
||||
?disabled="${(this.pages?.next || 0) <= 0}"
|
||||
aria-label="${gettext("Go to next page")}"
|
||||
>
|
||||
<i class="fas fa-angle-right" aria-hidden="true"></i>
|
||||
|
@ -8,7 +8,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
|
||||
new SidebarItem("Library", "/library"),
|
||||
new SidebarItem("Monitor").children(
|
||||
new SidebarItem("Overview", "/administration/overview"),
|
||||
new SidebarItem("System Tasks", "/administration/tasks/"),
|
||||
new SidebarItem("System Tasks", "/administration/system-tasks"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
@ -20,37 +20,37 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
new SidebarItem("Resources").children(
|
||||
new SidebarItem("Applications", "/applications").activeWhen(
|
||||
`^/applications/(?<slug>${SLUG_REGEX})$`
|
||||
new SidebarItem("Applications", "/core/applications").activeWhen(
|
||||
`^/core/applications/(?<slug>${SLUG_REGEX})$`
|
||||
),
|
||||
new SidebarItem("Sources", "/sources").activeWhen(
|
||||
`^/sources/(?<slug>${SLUG_REGEX})$`,
|
||||
new SidebarItem("Sources", "/core/sources").activeWhen(
|
||||
`^/core/sources/(?<slug>${SLUG_REGEX})$`,
|
||||
),
|
||||
new SidebarItem("Providers", "/providers"),
|
||||
new SidebarItem("Outposts", "/outposts"),
|
||||
new SidebarItem("Outpost Service Connections", "/administration/outpost_service_connections/"),
|
||||
new SidebarItem("Providers", "/core/providers"),
|
||||
new SidebarItem("Outposts", "/outpost/outposts"),
|
||||
new SidebarItem("Outpost Service Connections", "/outpost/service-connections"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
new SidebarItem("Customisation").children(
|
||||
new SidebarItem("Policies", "/administration/policies/"),
|
||||
new SidebarItem("Property Mappings", "/property-mappings"),
|
||||
new SidebarItem("Policies", "/policy/policies"),
|
||||
new SidebarItem("Property Mappings", "/core/property-mappings"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
new SidebarItem("Flows").children(
|
||||
new SidebarItem("Flows", "/flows").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})$`),
|
||||
new SidebarItem("Stages", "/administration/stages/"),
|
||||
new SidebarItem("Prompts", "/administration/stages_prompts/"),
|
||||
new SidebarItem("Invitations", "/administration/stages/invitations/"),
|
||||
new SidebarItem("Flows", "/flow/flows").activeWhen(`^/flow/flows/(?<slug>${SLUG_REGEX})$`),
|
||||
new SidebarItem("Stages", "/flow/stages"),
|
||||
new SidebarItem("Prompts", "/flow/stages/prompts"),
|
||||
new SidebarItem("Invitations", "/flow/stages/invitations"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
new SidebarItem("Identity & Cryptography").children(
|
||||
new SidebarItem("User", "/administration/users/"),
|
||||
new SidebarItem("Groups", "/administration/groups/"),
|
||||
new SidebarItem("User", "/identity/users"),
|
||||
new SidebarItem("Groups", "/identity/groups"),
|
||||
new SidebarItem("Certificates", "/crypto/certificates"),
|
||||
new SidebarItem("Tokens", "/administration/tokens/"),
|
||||
new SidebarItem("Tokens", "/core/tokens"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
}),
|
||||
|
@ -28,6 +28,7 @@ import "./pages/admin-overview/AdminOverviewPage";
|
||||
import "./pages/admin-overview/TopApplicationsTable";
|
||||
import "./pages/applications/ApplicationListPage";
|
||||
import "./pages/applications/ApplicationViewPage";
|
||||
import "./pages/tokens/UserTokenList";
|
||||
import "./pages/LibraryPage";
|
||||
|
||||
import "./elements/stages/authenticator_webauthn/WebAuthnRegister";
|
||||
|
@ -14,6 +14,10 @@ export class LibraryApplication extends LitElement {
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES.concat(
|
||||
css`
|
||||
:host,
|
||||
main {
|
||||
height: 100%;
|
||||
}
|
||||
a {
|
||||
height: 100%;
|
||||
}
|
||||
|
@ -36,7 +36,7 @@ export class AdminOverviewPage extends LitElement {
|
||||
<ak-aggregate-card class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-server" header="Apps with most usage" style="grid-column-end: span 2;grid-row-end: span 3;">
|
||||
<ak-top-applications-table></ak-top-applications-table>
|
||||
</ak-aggregate-card>
|
||||
<ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/providers/">
|
||||
<ak-admin-status-card-provider class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-plugged" header="Providers" headerLink="#/core/providers/">
|
||||
</ak-admin-status-card-provider>
|
||||
<ak-admin-status-card-policy-unbound class="pf-l-gallery__item pf-m-4-col" icon="pf-icon pf-icon-infrastructure" header="Policies" headerLink="#/administration/policies/">
|
||||
</ak-admin-status-card-policy-unbound>
|
||||
|
@ -50,7 +50,7 @@ export class ApplicationListPage extends TablePage<Application> {
|
||||
item.meta_icon ?
|
||||
html`<img class="app-icon pf-c-avatar" src="${item.meta_icon}" alt="${gettext("Application Icon")}">` :
|
||||
html`<i class="pf-icon pf-icon-arrow"></i>`,
|
||||
html`<a href="#/applications/${item.slug}">
|
||||
html`<a href="#/core/applications/${item.slug}">
|
||||
<div>
|
||||
${item.name}
|
||||
</div>
|
||||
|
@ -38,7 +38,14 @@ export class ApplicationViewPage extends LitElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.application) {
|
||||
return html`<ak-loading-state></ak-loading-state>`;
|
||||
return html`<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
<h1>
|
||||
${gettext("Loading...")}
|
||||
</h1>
|
||||
</div>
|
||||
</section>
|
||||
<ak-loading-state></ak-loading-state>`;
|
||||
}
|
||||
return html`<section class="pf-c-page__main-section pf-m-light">
|
||||
<div class="pf-c-content">
|
||||
@ -80,7 +87,7 @@ export class ApplicationViewPage extends LitElement {
|
||||
</dt>
|
||||
<dd class="pf-c-description-list__description">
|
||||
<div class="pf-c-description-list__text">
|
||||
<a href="#/providers/${this.application.provider.pk}">
|
||||
<a href="#/core/providers/${this.application.provider.pk}">
|
||||
${this.application.provider.name}
|
||||
</a>
|
||||
</div>
|
||||
|
@ -107,7 +107,7 @@ export class EventInfo extends LitElement {
|
||||
<span>${until(Flow.list({
|
||||
flow_uuid: this.event.context.flow as string,
|
||||
}).then(resp => {
|
||||
return html`<a href="#/flows/${resp.results[0].slug}">${resp.results[0].name}</a>`;
|
||||
return html`<a href="#/flow/flows/${resp.results[0].slug}">${resp.results[0].name}</a>`;
|
||||
}), html`<ak-spinner size=${SpinnerSize.Medium}></ak-spinner>`)}
|
||||
</span>
|
||||
</div>
|
||||
|
@ -47,7 +47,7 @@ export class FlowListPage extends TablePage<Flow> {
|
||||
|
||||
row(item: Flow): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/flows/${item.slug}">
|
||||
html`<a href="#/flow/flows/${item.slug}">
|
||||
<code>${item.slug}</code>
|
||||
</a>`,
|
||||
html`${item.name}`,
|
||||
|
80
web/src/pages/groups/GroupListPage.ts
Normal file
80
web/src/pages/groups/GroupListPage.ts
Normal file
@ -0,0 +1,80 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Group } from "../../api/Groups";
|
||||
|
||||
@customElement("ak-group-list")
|
||||
export class GroupListPage extends TablePage<Group> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Groups");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Group users together and give them permissions based on the membership.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-users");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "slug";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Group>> {
|
||||
return Group.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "name"),
|
||||
new TableColumn("Parent", "parent"),
|
||||
new TableColumn("Members"),
|
||||
new TableColumn("Superuser privileges?"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Group): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.parent || "-"}`,
|
||||
html`${item.users.length}`,
|
||||
html`${item.is_superuser ? "Yes" : "No"}`,
|
||||
html`
|
||||
<ak-modal-button href="${Group.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Group.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${Group.adminUrl("create/")}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Create")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ export class OutpostListPage extends TablePage<Outpost> {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ul>${item.providers_obj.map((p) => {
|
||||
return html`<li><a href="#/providers/${p.pk}">${p.name}</a></li>`;
|
||||
return html`<li><a href="#/core/providers/${p.pk}">${p.name}</a></li>`;
|
||||
})}</ul>`,
|
||||
html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`,
|
||||
html`
|
||||
|
102
web/src/pages/outposts/OutpostServiceConnectionListPage.ts
Normal file
102
web/src/pages/outposts/OutpostServiceConnectionListPage.ts
Normal file
@ -0,0 +1,102 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { OutpostServiceConnection } from "../../api/Outposts";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "./OutpostHealth";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { until } from "lit-html/directives/until";
|
||||
|
||||
@customElement("ak-outpost-service-connection-list")
|
||||
export class OutpostServiceConnectionListPage extends TablePage<OutpostServiceConnection> {
|
||||
pageTitle(): string {
|
||||
return "Outpost Service-Connections";
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
return "Outpost Service-Connections define how authentik connects to external platforms to manage and deploy Outposts.";
|
||||
}
|
||||
pageIcon(): string {
|
||||
return "pf-icon pf-icon-integration";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<OutpostServiceConnection>> {
|
||||
return OutpostServiceConnection.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "name"),
|
||||
new TableColumn("Type"),
|
||||
new TableColumn("Local", "local"),
|
||||
new TableColumn("State"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
row(item: OutpostServiceConnection): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`${item.verbose_name}`,
|
||||
html`${item.local ? "Yes" : "No"}`,
|
||||
html`${until(OutpostServiceConnection.state(item.pk).then((state) => {
|
||||
if (state.healthy) {
|
||||
return html`<i class="fas fa-check pf-m-success"></i> ${state.version}`;
|
||||
}
|
||||
return html`<i class="fas fa-times pf-m-danger"></i> ${gettext("Unhealthy")}`;
|
||||
}), html`<ak-spinner></ak-spinner>`)}`,
|
||||
html`
|
||||
<ak-modal-button href="${OutpostServiceConnection.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${OutpostServiceConnection.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${gettext("Create")}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
${until(OutpostServiceConnection.getTypes().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-modal-button href="${type.link}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
108
web/src/pages/policies/PolicyListPage.ts
Normal file
108
web/src/pages/policies/PolicyListPage.ts
Normal file
@ -0,0 +1,108 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Policy } from "../../api/Policies";
|
||||
import { until } from "lit-html/directives/until";
|
||||
|
||||
@customElement("ak-policy-list")
|
||||
export class PolicyListPage extends TablePage<Policy> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Policies");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Stages.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-infrastructure");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Policy>> {
|
||||
return Policy.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "name"),
|
||||
new TableColumn("Type"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Policy): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.name}</div>
|
||||
${item.bound_to > 0 ?
|
||||
html`<i class="pf-icon pf-icon-ok"></i>
|
||||
<small>
|
||||
${gettext(`Assigned to ${item.bound_to} objects.`)}
|
||||
</small>`:
|
||||
html`<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
<small>${gettext("Warning: Policy is not assigned.")}</small>`}
|
||||
</div>`,
|
||||
html`${item.verbose_name}`,
|
||||
html`
|
||||
<ak-modal-button href="${Policy.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Policy.adminUrl(`${item.pk}/test/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Test")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Policy.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${gettext("Create")}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
${until(Policy.getTypes().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-modal-button href="${type.link}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
@ -8,6 +8,7 @@ import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { until } from "lit-html/directives/until";
|
||||
|
||||
@customElement("ak-property-mapping-list")
|
||||
export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
@ -82,36 +83,18 @@ export class PropertyMappingListPage extends TablePage<PropertyMapping> {
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
<li>
|
||||
<ak-modal-button href="${PropertyMapping.adminUrl("create/?type=LDAPPropertyMapping")}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("LDAP Property Mapping")}<br>
|
||||
<small>
|
||||
${gettext("Map LDAP Property to User or Group object attribute")}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
<li>
|
||||
<ak-modal-button href="${PropertyMapping.adminUrl("create/?type=SAMLPropertyMapping")}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("SAML Property Mapping")}<br>
|
||||
<small>
|
||||
${gettext("Map User/Group attribute to SAML Attribute, which can be used by the Service Provider.")}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
<li>
|
||||
<ak-modal-button href="${PropertyMapping.adminUrl("create/?type=ScopeMapping")}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${gettext("Scope Mapping")}<br>
|
||||
<small>
|
||||
${gettext("Map an OAuth Scope to users properties")}
|
||||
</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
${until(PropertyMapping.getTypes().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-modal-button href="${type.link}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
|
@ -47,13 +47,13 @@ export class ProviderListPage extends TablePage<Provider> {
|
||||
|
||||
row(item: Provider): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/providers/${item.pk}">
|
||||
html`<a href="#/core/providers/${item.pk}">
|
||||
${item.name}
|
||||
</a>`,
|
||||
item.assigned_application_name ?
|
||||
html`<i class="pf-icon pf-icon-ok"></i>
|
||||
${gettext("Assigned to application ")}
|
||||
<a href="#/applications/${item.assigned_application_slug}">${item.assigned_application_name}</a>` :
|
||||
<a href="#/core/applications/${item.assigned_application_slug}">${item.assigned_application_name}</a>` :
|
||||
html`<i class="pf-icon pf-icon-warning-triangle"></i>
|
||||
${gettext("Warning: Provider not assigned to any application.")}`,
|
||||
html`${item.verbose_name}`,
|
||||
|
@ -14,7 +14,7 @@ export class RelatedApplicationButton extends LitElement {
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.provider?.assigned_application_slug) {
|
||||
return html`<a href="#/applications/${this.provider.assigned_application_slug}">
|
||||
return html`<a href="#/core/applications/${this.provider.assigned_application_slug}">
|
||||
${this.provider.assigned_application_name}
|
||||
</a>`;
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export class SourceListPage extends TablePage<Source> {
|
||||
|
||||
row(item: Source): TemplateResult[] {
|
||||
return [
|
||||
html`<a href="#/sources/${item.slug}">
|
||||
html`<a href="#/core/sources/${item.slug}">
|
||||
<div>${item.name}</div>
|
||||
${item.enabled ? html`` : html`<small>${gettext("Disabled")}</small>`}
|
||||
</a>`,
|
||||
|
72
web/src/pages/stages/InvitationListPage.ts
Normal file
72
web/src/pages/stages/InvitationListPage.ts
Normal file
@ -0,0 +1,72 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Invitation } from "../../api/Invitations";
|
||||
|
||||
@customElement("ak-stage-invitation-list")
|
||||
export class InvitationListPage extends TablePage<Invitation> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Invitations");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Create Invitation Links to enroll Users, and optionally force specific attributes of their account.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-migration");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "expires";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Invitation>> {
|
||||
return Invitation.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("ID", "pk"),
|
||||
new TableColumn("Created by", "created_by"),
|
||||
new TableColumn("Expiry"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Invitation): TemplateResult[] {
|
||||
return [
|
||||
html`${item.pk}`,
|
||||
html`${item.created_by.username}`,
|
||||
html`${new Date(item.expires * 1000).toLocaleString()}`,
|
||||
html`
|
||||
<ak-modal-button href="${Invitation.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${Invitation.adminUrl("create/")}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Create")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
84
web/src/pages/stages/PromptListPage.ts
Normal file
84
web/src/pages/stages/PromptListPage.ts
Normal file
@ -0,0 +1,84 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Prompt } from "../../api/Prompts";
|
||||
|
||||
@customElement("ak-stage-prompt-list")
|
||||
export class PromptListPage extends TablePage<Prompt> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Prompts");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Single Prompts that can be used for Prompt Stages.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-plugged");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "order";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Prompt>> {
|
||||
return Prompt.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Field", "field_key"),
|
||||
new TableColumn("Label", "label"),
|
||||
new TableColumn("Type", "type"),
|
||||
new TableColumn("Order", "order"),
|
||||
new TableColumn("Stages"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Prompt): TemplateResult[] {
|
||||
return [
|
||||
html`${item.field_key}`,
|
||||
html`${item.label}`,
|
||||
html`${item.type}`,
|
||||
html`${item.order}`,
|
||||
html`${item.promptstage_set.map((stage) => {
|
||||
return html`<li>${stage.name}</li>`;
|
||||
})}`,
|
||||
html`
|
||||
<ak-modal-button href="${Prompt.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Prompt.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${Prompt.adminUrl("create/")}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Create")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
100
web/src/pages/stages/StageListPage.ts
Normal file
100
web/src/pages/stages/StageListPage.ts
Normal file
@ -0,0 +1,100 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Stage } from "../../api/Flows";
|
||||
|
||||
@customElement("ak-stage-list")
|
||||
export class StageListPage extends TablePage<Stage> {
|
||||
pageTitle(): string {
|
||||
return "Stages";
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
return "Stages are single steps of a Flow that a user is guided through.";
|
||||
}
|
||||
pageIcon(): string {
|
||||
return "pf-icon pf-icon-plugged";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Stage>> {
|
||||
return Stage.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "name"),
|
||||
new TableColumn("Flows"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Stage): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.name}</div>
|
||||
<small>${item.verbose_name}</small>
|
||||
</div>`,
|
||||
html`${item.flow_set.map((flow) => {
|
||||
return html`<a href="#/flow/flows/${flow.slug}">
|
||||
<code>${flow.slug}</code>
|
||||
</a>`;
|
||||
})}`,
|
||||
html`
|
||||
<ak-modal-button href="${Stage.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Stage.adminUrl(`${item.pk}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-m-primary pf-c-dropdown__toggle" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${gettext("Create")}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
${until(Stage.getTypes().then((types) => {
|
||||
return types.map((type) => {
|
||||
return html`<li>
|
||||
<ak-modal-button href="${type.link}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">${type.name}<br>
|
||||
<small>${type.description}</small>
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
${super.renderToolbar()}`;
|
||||
}
|
||||
|
||||
}
|
87
web/src/pages/system-tasks/SystemTaskListPage.ts
Normal file
87
web/src/pages/system-tasks/SystemTaskListPage.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/SpinnerButton";
|
||||
import "../../elements/buttons/ActionButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { SystemTask, TaskStatus } from "../../api/SystemTask";
|
||||
|
||||
@customElement("ak-system-task-list")
|
||||
export class SystemTaskListPage extends TablePage<SystemTask> {
|
||||
searchEnabled(): boolean {
|
||||
return false;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("System Tasks");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Long-running operations which authentik executes in the background.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-automation");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "slug";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<SystemTask>> {
|
||||
return SystemTask.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
}).then((tasks) => {
|
||||
return {
|
||||
pagination: {
|
||||
count: tasks.length,
|
||||
total_pages: 1,
|
||||
start_index: 0,
|
||||
end_index: tasks.length,
|
||||
current: 1,
|
||||
},
|
||||
results: tasks,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Identifier", "task_name"),
|
||||
new TableColumn("Description"),
|
||||
new TableColumn("Last run"),
|
||||
new TableColumn("Status"),
|
||||
new TableColumn("Messages"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
taskStatus(task: SystemTask): TemplateResult {
|
||||
switch (task.status) {
|
||||
case TaskStatus.SUCCESSFUL:
|
||||
return html`<i class="fas fa-check pf-m-success" > </i> ${gettext("Successful")}`;
|
||||
case TaskStatus.WARNING:
|
||||
return html`<i class="fas fa-exclamation-triangle pf-m-warning" > </i> ${gettext("Warning")}`;
|
||||
case TaskStatus.ERROR:
|
||||
return html`<i class="fas fa-times pf-m-danger" > </i> ${gettext("Error")}`;
|
||||
default:
|
||||
return html`<i class="fas fa-question-circle" > </i> ${gettext("Unknown")}`;
|
||||
}
|
||||
}
|
||||
|
||||
row(item: SystemTask): TemplateResult[] {
|
||||
return [
|
||||
html`${item.task_name}`,
|
||||
html`${item.task_description}`,
|
||||
html`${new Date(item.task_finish_timestamp * 1000).toLocaleString()}`,
|
||||
this.taskStatus(item),
|
||||
html`${item.messages.map(m => {
|
||||
return html`<li>${m}</li>`;
|
||||
})}`,
|
||||
html`<ak-action-button url=${SystemTask.retry(item.task_name)}>
|
||||
${gettext("Retry Task")}
|
||||
</ak-action-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
68
web/src/pages/tokens/TokenListPage.ts
Normal file
68
web/src/pages/tokens/TokenListPage.ts
Normal file
@ -0,0 +1,68 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/TokenCopyButton";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { Token } from "../../api/Tokens";
|
||||
|
||||
@customElement("ak-token-list")
|
||||
export class TokenListPage extends TablePage<Token> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Tokens");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return gettext("Tokens are used throughout authentik for Email validation stages, Recovery keys and API access.");
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-security");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "expires";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Token>> {
|
||||
return Token.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Identifier", "identifier"),
|
||||
new TableColumn("User", "user"),
|
||||
new TableColumn("Expires?", "expiring"),
|
||||
new TableColumn("Expiry date", "expires"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Token): TemplateResult[] {
|
||||
return [
|
||||
html`${item.identifier}`,
|
||||
html`${item.user.username}`,
|
||||
html`${item.expiring ? "Yes" : "No"}`,
|
||||
html`${item.expiring ? new Date(item.expires * 1000).toLocaleString() : "-"}`,
|
||||
html`
|
||||
<ak-modal-button href="${Token.adminUrl(`${item.identifier}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-token-copy-button identifier="${item.identifier}">
|
||||
${gettext("Copy Key")}
|
||||
</ak-token-copy-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
64
web/src/pages/tokens/UserTokenList.ts
Normal file
64
web/src/pages/tokens/UserTokenList.ts
Normal file
@ -0,0 +1,64 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import "../../elements/buttons/TokenCopyButton";
|
||||
import { Table, TableColumn } from "../../elements/table/Table";
|
||||
import { Token } from "../../api/Tokens";
|
||||
|
||||
@customElement("ak-token-user-list")
|
||||
export class UserTokenList extends Table<Token> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "expires";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<Token>> {
|
||||
return Token.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Identifier", "identifier"),
|
||||
new TableColumn("User", "user"),
|
||||
new TableColumn("Expires?", "expiring"),
|
||||
new TableColumn("Expiry date", "expires"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: Token): TemplateResult[] {
|
||||
return [
|
||||
html`${item.identifier}`,
|
||||
html`${item.user.username}`,
|
||||
html`${item.expiring ? "Yes" : "No"}`,
|
||||
html`${item.expiring ? new Date(item.expires * 1000).toLocaleString() : "-"}`,
|
||||
html`
|
||||
<ak-modal-button href="${Token.userUrl(`${item.identifier}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-modal-button href="${Token.userUrl(`${item.identifier}/delete/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-danger">
|
||||
${gettext("Delete")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-token-copy-button identifier="${item.identifier}">
|
||||
${gettext("Copy Key")}
|
||||
</ak-token-copy-button>
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
}
|
113
web/src/pages/users/UserListPage.ts
Normal file
113
web/src/pages/users/UserListPage.ts
Normal file
@ -0,0 +1,113 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, html, property, TemplateResult } from "lit-element";
|
||||
import { AKResponse } from "../../api/Client";
|
||||
import { TablePage } from "../../elements/table/TablePage";
|
||||
|
||||
import "../../elements/buttons/ModalButton";
|
||||
import "../../elements/buttons/Dropdown";
|
||||
import { TableColumn } from "../../elements/table/Table";
|
||||
import { User } from "../../api/Users";
|
||||
|
||||
@customElement("ak-user-list")
|
||||
export class UserListPage extends TablePage<User> {
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
pageTitle(): string {
|
||||
return gettext("Users");
|
||||
}
|
||||
pageDescription(): string {
|
||||
return "";
|
||||
}
|
||||
pageIcon(): string {
|
||||
return gettext("pf-icon pf-icon-user");
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "username";
|
||||
|
||||
apiEndpoint(page: number): Promise<AKResponse<User>> {
|
||||
return User.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "username"),
|
||||
new TableColumn("Active", "active"),
|
||||
new TableColumn("Last login", "last_login"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
row(item: User): TemplateResult[] {
|
||||
return [
|
||||
html`<div>
|
||||
<div>${item.username}</div>
|
||||
<small>${item.name}</small>
|
||||
</div>`,
|
||||
html`${item.is_active ? "Yes" : "No"}`,
|
||||
html`${new Date(item.last_login * 1000).toLocaleString()}`,
|
||||
html`
|
||||
<ak-modal-button href="${User.adminUrl(`${item.pk}/update/`)}">
|
||||
<ak-spinner-button slot="trigger" class="pf-m-secondary">
|
||||
${gettext("Edit")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
<ak-dropdown class="pf-c-dropdown">
|
||||
<button class="pf-c-dropdown__toggle pf-m-primary" type="button">
|
||||
<span class="pf-c-dropdown__toggle-text">${gettext(item.is_active ? "Disable" : "Enable")}</span>
|
||||
<i class="fas fa-caret-down pf-c-dropdown__toggle-icon" aria-hidden="true"></i>
|
||||
</button>
|
||||
<ul class="pf-c-dropdown__menu" hidden>
|
||||
<li>
|
||||
${item.is_active ?
|
||||
html`<ak-modal-button href="${User.adminUrl(`${item.pk}/disable/`)}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${gettext("Disable")}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`:
|
||||
html`<ak-modal-button href="${User.adminUrl(`${item.pk}/enable/`)}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${gettext("Enable")}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>`}
|
||||
</li>
|
||||
<li class="pf-c-divider" role="separator"></li>
|
||||
<li>
|
||||
<ak-modal-button href="${User.adminUrl(`${item.pk}/delete/`)}">
|
||||
<button slot="trigger" class="pf-c-dropdown__menu-item">
|
||||
${gettext("Delete")}
|
||||
</button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
</li>
|
||||
</ul>
|
||||
</ak-dropdown>
|
||||
<a class="pf-c-button pf-m-tertiary" href="${User.adminUrl(`${item.pk}/reset/`)}">
|
||||
${gettext("Reset Password")}
|
||||
</a>
|
||||
<a class="pf-c-button pf-m-tertiary" href="${`-/impersonation/${item.pk}/`}">
|
||||
${gettext("Impersonate")}
|
||||
</a>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${User.adminUrl("create/")}>
|
||||
<ak-spinner-button slot="trigger" class="pf-m-primary">
|
||||
${gettext("Create")}
|
||||
</ak-spinner-button>
|
||||
<div slot="modal"></div>
|
||||
</ak-modal-button>
|
||||
${super.renderToolbar()}
|
||||
`;
|
||||
}
|
||||
}
|
@ -1,23 +1,32 @@
|
||||
import { html } from "lit-html";
|
||||
import { Route, SLUG_REGEX, ID_REGEX, UUID_REGEX } from "./elements/router/Route";
|
||||
|
||||
import "./pages/LibraryPage";
|
||||
import "./pages/admin-overview/AdminOverviewPage";
|
||||
import "./pages/applications/ApplicationListPage";
|
||||
import "./pages/applications/ApplicationViewPage";
|
||||
import "./pages/sources/SourcesListPage";
|
||||
import "./pages/sources/SourceViewPage";
|
||||
import "./pages/crypto/CertificateKeyPairListPage";
|
||||
import "./pages/events/EventInfoPage";
|
||||
import "./pages/events/EventListPage";
|
||||
import "./pages/events/RuleListPage";
|
||||
import "./pages/events/TransportListPage";
|
||||
import "./pages/flows/FlowListPage";
|
||||
import "./pages/flows/FlowViewPage";
|
||||
import "./pages/events/EventListPage";
|
||||
import "./pages/events/EventInfoPage";
|
||||
import "./pages/events/TransportListPage";
|
||||
import "./pages/events/RuleListPage";
|
||||
import "./pages/groups/GroupListPage";
|
||||
import "./pages/LibraryPage";
|
||||
import "./pages/outposts/OutpostListPage";
|
||||
import "./pages/outposts/OutpostServiceConnectionListPage";
|
||||
import "./pages/policies/PolicyListPage";
|
||||
import "./pages/property-mappings/PropertyMappingListPage";
|
||||
import "./pages/providers/ProviderListPage";
|
||||
import "./pages/providers/ProviderViewPage";
|
||||
import "./pages/property-mappings/PropertyMappingListPage";
|
||||
import "./pages/outposts/OutpostListPage";
|
||||
import "./pages/crypto/CertificateKeyPairListPage";
|
||||
import "./pages/sources/SourcesListPage";
|
||||
import "./pages/sources/SourceViewPage";
|
||||
import "./pages/stages/StageListPage";
|
||||
import "./pages/stages/InvitationListPage";
|
||||
import "./pages/stages/PromptListPage";
|
||||
import "./pages/system-tasks/SystemTaskListPage";
|
||||
import "./pages/tokens/TokenListPage";
|
||||
import "./pages/users/UserListPage";
|
||||
|
||||
export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
@ -25,20 +34,28 @@ export const ROUTES: Route[] = [
|
||||
new Route(new RegExp("^#.*")).redirect("/library"),
|
||||
new Route(new RegExp("^/library$"), html`<ak-library></ak-library>`),
|
||||
new Route(new RegExp("^/administration/overview$"), html`<ak-admin-overview></ak-admin-overview>`),
|
||||
new Route(new RegExp("^/providers$"), html`<ak-provider-list></ak-provider-list>`),
|
||||
new Route(new RegExp(`^/providers/(?<id>${ID_REGEX})$`)).then((args) => {
|
||||
new Route(new RegExp("^/administration/system-tasks$"), html`<ak-system-task-list></ak-system-task-list>`),
|
||||
new Route(new RegExp("^/core/providers$"), html`<ak-provider-list></ak-provider-list>`),
|
||||
new Route(new RegExp(`^/core/providers/(?<id>${ID_REGEX})$`)).then((args) => {
|
||||
return html`<ak-provider-view .providerID=${parseInt(args.id, 10)}></ak-provider-view>`;
|
||||
}),
|
||||
new Route(new RegExp("^/applications$"), html`<ak-application-list></ak-application-list>`),
|
||||
new Route(new RegExp(`^/applications/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
new Route(new RegExp("^/core/applications$"), html`<ak-application-list></ak-application-list>`),
|
||||
new Route(new RegExp(`^/core/applications/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
return html`<ak-application-view .args=${args}></ak-application-view>`;
|
||||
}),
|
||||
new Route(new RegExp("^/sources$"), html`<ak-source-list></ak-source-list>`),
|
||||
new Route(new RegExp(`^/sources/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
new Route(new RegExp("^/core/sources$"), html`<ak-source-list></ak-source-list>`),
|
||||
new Route(new RegExp(`^/core/sources/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
return html`<ak-source-view .args=${args}></ak-source-view>`;
|
||||
}),
|
||||
new Route(new RegExp("^/flows$"), html`<ak-flow-list></ak-flow-list>`),
|
||||
new Route(new RegExp(`^/flows/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
new Route(new RegExp("^/policy/policies$"), html`<ak-policy-list></ak-policy-list>`),
|
||||
new Route(new RegExp("^/identity/groups$"), html`<ak-group-list></ak-group-list>`),
|
||||
new Route(new RegExp("^/identity/users$"), html`<ak-user-list></ak-user-list>`),
|
||||
new Route(new RegExp("^/core/tokens$"), html`<ak-token-list></ak-token-list>`),
|
||||
new Route(new RegExp("^/flow/stages/invitations$"), html`<ak-stage-invitation-list></ak-stage-invitation-list>`),
|
||||
new Route(new RegExp("^/flow/stages/prompts$"), html`<ak-stage-prompt-list></ak-stage-prompt-list>`),
|
||||
new Route(new RegExp("^/flow/stages$"), html`<ak-stage-list></ak-stage-list>`),
|
||||
new Route(new RegExp("^/flow/flows$"), html`<ak-flow-list></ak-flow-list>`),
|
||||
new Route(new RegExp(`^/flow/flows/(?<slug>${SLUG_REGEX})$`)).then((args) => {
|
||||
return html`<ak-flow-view .flowSlug=${args.slug}></ak-flow-view>`;
|
||||
}),
|
||||
new Route(new RegExp("^/events/log$"), html`<ak-event-list></ak-event-list>`),
|
||||
@ -47,7 +64,8 @@ export const ROUTES: Route[] = [
|
||||
}),
|
||||
new Route(new RegExp("^/events/transports$"), html`<ak-event-transport-list></ak-event-transport-list>`),
|
||||
new Route(new RegExp("^/events/rules$"), html`<ak-event-rule-list></ak-event-rule-list>`),
|
||||
new Route(new RegExp("^/property-mappings$"), html`<ak-property-mapping-list></ak-property-mapping-list>`),
|
||||
new Route(new RegExp("^/outposts$"), html`<ak-outpost-list></ak-outpost-list>`),
|
||||
new Route(new RegExp("^/core/property-mappings$"), html`<ak-property-mapping-list></ak-property-mapping-list>`),
|
||||
new Route(new RegExp("^/outpost/outposts$"), html`<ak-outpost-list></ak-outpost-list>`),
|
||||
new Route(new RegExp("^/outpost/service-connections$"), html`<ak-outpost-service-connection-list></ak-outpost-service-connection-list>`),
|
||||
new Route(new RegExp("^/crypto/certificates$"), html`<ak-crypto-certificatekeypair-list></ak-crypto-certificatekeypair-list>`),
|
||||
];
|
||||
|
Reference in New Issue
Block a user