Merge branch 'master' into version-2021.2
This commit is contained in:
6
web/package-lock.json
generated
6
web/package-lock.json
generated
@ -899,9 +899,9 @@
|
||||
"integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
|
||||
},
|
||||
"construct-style-sheets-polyfill": {
|
||||
"version": "2.4.6",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.6.tgz",
|
||||
"integrity": "sha512-lU0to7dFDjKslMF+M5NUa4s0RQMBRVyZMXvD/vp7vmjdEPgziTkHSfZHQxfoIvVWajWRJUVJMLfrMwcx8fTh4A=="
|
||||
"version": "2.4.9",
|
||||
"resolved": "https://registry.npmjs.org/construct-style-sheets-polyfill/-/construct-style-sheets-polyfill-2.4.9.tgz",
|
||||
"integrity": "sha512-kPXZXxsp7CTr/Vs29+omUA29wTrFplkdY6jqxyv0DDWC5Ro79WmwpboH2M9KiOclbtn8r81GCFtc7+t7OjRnCw=="
|
||||
},
|
||||
"copy-descriptor": {
|
||||
"version": "0.1.1",
|
||||
|
||||
@ -18,7 +18,7 @@
|
||||
"@types/codemirror": "0.0.108",
|
||||
"chart.js": "^2.9.4",
|
||||
"codemirror": "^5.59.2",
|
||||
"construct-style-sheets-polyfill": "^2.4.6",
|
||||
"construct-style-sheets-polyfill": "^2.4.9",
|
||||
"flowchart.js": "^1.15.0",
|
||||
"lit-element": "^2.4.0",
|
||||
"lit-html": "^1.3.0",
|
||||
|
||||
@ -7,6 +7,13 @@ export interface QueryArguments {
|
||||
[key: string]: number | string | boolean | null;
|
||||
}
|
||||
|
||||
export interface BaseInheritanceModel {
|
||||
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
|
||||
}
|
||||
|
||||
export class Client {
|
||||
makeUrl(url: string[], query?: QueryArguments): string {
|
||||
let builtUrl = `/api/${VERSION}/${url.join("/")}/`;
|
||||
|
||||
40
web/src/api/Outposts.ts
Normal file
40
web/src/api/Outposts.ts
Normal file
@ -0,0 +1,40 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
import { Provider } from "./Providers";
|
||||
|
||||
export interface OutpostHealth {
|
||||
last_seen: number;
|
||||
version: string;
|
||||
version_should: string;
|
||||
version_outdated: boolean;
|
||||
}
|
||||
|
||||
export class Outpost {
|
||||
|
||||
pk: string;
|
||||
name: string;
|
||||
providers: number[];
|
||||
providers_obj: Provider[];
|
||||
service_connection?: string;
|
||||
_config: QueryArguments;
|
||||
token_identifier: string;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
|
||||
static get(pk: string): Promise<Outpost> {
|
||||
return DefaultClient.fetch<Outpost>(["outposts", "outposts", pk]);
|
||||
}
|
||||
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<Outpost>> {
|
||||
return DefaultClient.fetch<PBResponse<Outpost>>(["outposts", "outposts"], filter);
|
||||
}
|
||||
|
||||
static health(pk: string): Promise<OutpostHealth[]> {
|
||||
return DefaultClient.fetch<OutpostHealth[]>(["outposts", "outposts", pk, "health"]);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/outposts/${rest}`;
|
||||
}
|
||||
}
|
||||
@ -1,12 +1,14 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
import { DefaultClient, BaseInheritanceModel, PBResponse, QueryArguments } from "./Client";
|
||||
|
||||
export class Policy {
|
||||
export class Policy implements BaseInheritanceModel {
|
||||
pk: string;
|
||||
name: string;
|
||||
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
|
||||
static get(pk: string): Promise<Policy> {
|
||||
return DefaultClient.fetch<Policy>(["policies", "all", pk]);
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
import { BaseInheritanceModel, DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
|
||||
export class Provider {
|
||||
export class Provider implements BaseInheritanceModel {
|
||||
pk: number;
|
||||
name: string;
|
||||
authorization_flow: string;
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
import { DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
import { BaseInheritanceModel, DefaultClient, PBResponse, QueryArguments } from "./Client";
|
||||
|
||||
export class Source {
|
||||
export class Source implements BaseInheritanceModel {
|
||||
pk: string;
|
||||
name: string;
|
||||
slug: string;
|
||||
@ -11,6 +11,8 @@ export class Source {
|
||||
constructor() {
|
||||
throw Error();
|
||||
}
|
||||
verbose_name: string;
|
||||
verbose_name_plural: string;
|
||||
|
||||
static get(slug: string): Promise<Source> {
|
||||
return DefaultClient.fetch<Source>(["sources", "all", slug]);
|
||||
@ -19,4 +21,8 @@ export class Source {
|
||||
static list(filter?: QueryArguments): Promise<PBResponse<Source>> {
|
||||
return DefaultClient.fetch<PBResponse<Source>>(["sources", "all"], filter);
|
||||
}
|
||||
|
||||
static adminUrl(rest: string): string {
|
||||
return `/administration/sources/${rest}`;
|
||||
}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
|
||||
`^/sources/(?<slug>${SLUG_REGEX})$`,
|
||||
),
|
||||
new SidebarItem("Providers", "/providers"),
|
||||
new SidebarItem("Outposts", "/administration/outposts/"),
|
||||
new SidebarItem("Outposts", "/outposts"),
|
||||
new SidebarItem("Outpost Service Connections", "/administration/outpost_service_connections/"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
@ -41,7 +41,7 @@ export const SIDEBAR_ITEMS: SidebarItem[] = [
|
||||
new SidebarItem("Flows").children(
|
||||
new SidebarItem("Flows", "/administration/flows/").activeWhen(`^/flows/(?<slug>${SLUG_REGEX})$`),
|
||||
new SidebarItem("Stages", "/administration/stages/"),
|
||||
new SidebarItem("Prompts", "/administration/stages/prompts/"),
|
||||
new SidebarItem("Prompts", "/administration/stages_prompts/"),
|
||||
new SidebarItem("Invitations", "/administration/stages/invitations/"),
|
||||
).when((): Promise<boolean> => {
|
||||
return User.me().then(u => u.is_superuser);
|
||||
|
||||
@ -65,13 +65,13 @@ export class EventInfo extends LitElement {
|
||||
case "model_updated":
|
||||
case "model_deleted":
|
||||
return html`
|
||||
<h3>${gettext("Affected model:")}</h3><hr>
|
||||
<h3>${gettext("Affected model:")}</h3>
|
||||
${this.getModelInfo(this.event.context.model as EventContext)}
|
||||
`;
|
||||
case "authorize_application":
|
||||
return html`<div class="pf-l-flex">
|
||||
<div class="pf-l-flex__item">
|
||||
<h3>${gettext("Authorized application:")}</h3><hr>
|
||||
<h3>${gettext("Authorized application:")}</h3>
|
||||
${this.getModelInfo(this.event.context.authorized_application as EventContext)}
|
||||
</div>
|
||||
<div class="pf-l-flex__item">
|
||||
@ -83,14 +83,15 @@ export class EventInfo extends LitElement {
|
||||
}), html`<ak-spinner size=${SpinnerSize.Medium}></ak-spinner>`)}
|
||||
</span>
|
||||
</div>
|
||||
</div>`;
|
||||
</div>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case "login_failed":
|
||||
return html`
|
||||
<h3>${gettext(`Attempted to log in as ${this.event.context.username}`)}</h3>
|
||||
<ak-expand>${this.defaultResponse()}</ak-expand>`;
|
||||
case "token_view":
|
||||
return html`
|
||||
<h3>${gettext("Token:")}</h3><hr>
|
||||
<h3>${gettext("Token:")}</h3>
|
||||
${this.getModelInfo(this.event.context.token as EventContext)}`;
|
||||
case "property_mapping_exception":
|
||||
return html`<div class="pf-l-flex">
|
||||
|
||||
49
web/src/pages/outposts/OutpostHealth.ts
Normal file
49
web/src/pages/outposts/OutpostHealth.ts
Normal file
@ -0,0 +1,49 @@
|
||||
import { gettext } from "django";
|
||||
import { CSSResult, customElement, html, LitElement, property, TemplateResult } from "lit-element";
|
||||
import { until } from "lit-html/directives/until";
|
||||
import { Outpost } from "../../api/Outposts";
|
||||
import { COMMON_STYLES } from "../../common/styles";
|
||||
|
||||
@customElement("ak-outpost-health")
|
||||
export class OutpostHealth extends LitElement {
|
||||
|
||||
@property()
|
||||
outpostId?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return COMMON_STYLES;
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (!this.outpostId) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
return html`<ul>${until(Outpost.health(this.outpostId).then((oh) => {
|
||||
if (oh.length === 0) {
|
||||
return html`<li>
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<i class="fas fa-question-circle"></i> ${gettext("Not available")}
|
||||
</li>
|
||||
</ul>
|
||||
</li>`;
|
||||
}
|
||||
return oh.map((h) => {
|
||||
return html`<li>
|
||||
<ul>
|
||||
<li role="cell">
|
||||
<i class="fas fa-check pf-m-success"></i> ${gettext(`Last seen: ${new Date(h.last_seen * 1000).toLocaleTimeString()}`)}
|
||||
</li>
|
||||
<li role="cell">
|
||||
${h.version_outdated ?
|
||||
html`<i class="fas fa-times pf-m-danger"></i>
|
||||
${gettext(`${h.version}, should be ${h.version_should}`)}` :
|
||||
html`<i class="fas fa-check pf-m-success"></i> ${gettext(`Version: ${h.version}`)}`}
|
||||
</li>
|
||||
</ul>
|
||||
</li>`;
|
||||
});
|
||||
}), html`<ak-spinner></ak-spinner>`)}</ul>`;
|
||||
}
|
||||
|
||||
}
|
||||
121
web/src/pages/outposts/OutpostListPage.ts
Normal file
121
web/src/pages/outposts/OutpostListPage.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import { gettext } from "django";
|
||||
import { customElement, property } from "lit-element";
|
||||
import { html, TemplateResult } from "lit-html";
|
||||
import { PBResponse } from "../../api/Client";
|
||||
import { Outpost } 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";
|
||||
|
||||
@customElement("ak-outpost-list")
|
||||
export class OutpostListPage extends TablePage<Outpost> {
|
||||
pageTitle(): string {
|
||||
return "Outposts";
|
||||
}
|
||||
pageDescription(): string | undefined {
|
||||
return "Outposts are deployments of authentik components to support different environments and protocols, like reverse proxies.";
|
||||
}
|
||||
pageIcon(): string {
|
||||
return "pf-icon pf-icon-zone";
|
||||
}
|
||||
searchEnabled(): boolean {
|
||||
return true;
|
||||
}
|
||||
apiEndpoint(page: number): Promise<PBResponse<Outpost>> {
|
||||
return Outpost.list({
|
||||
ordering: this.order,
|
||||
page: page,
|
||||
search: this.search || "",
|
||||
});
|
||||
}
|
||||
columns(): TableColumn[] {
|
||||
return [
|
||||
new TableColumn("Name", "name"),
|
||||
new TableColumn("Providers"),
|
||||
new TableColumn("Health and Version"),
|
||||
new TableColumn(""),
|
||||
];
|
||||
}
|
||||
|
||||
@property()
|
||||
order = "name";
|
||||
|
||||
row(item: Outpost): TemplateResult[] {
|
||||
return [
|
||||
html`${item.name}`,
|
||||
html`<ul>${item.providers_obj.map((p) => {
|
||||
return html`<li><a href="#/providers/${p.pk}">${p.name}</a></li>`;
|
||||
})}</ul>`,
|
||||
html`<ak-outpost-health outpostId=${item.pk}></ak-outpost-health>`,
|
||||
html`
|
||||
<ak-modal-button href="${Outpost.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="${Outpost.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>
|
||||
<ak-modal-button>
|
||||
<button slot="trigger" class="pf-c-button pf-m-tertiary">
|
||||
${gettext("View Deployment Info")}
|
||||
</button>
|
||||
<div slot="modal">
|
||||
<div class="pf-c-modal-box__header">
|
||||
<h1 class="pf-c-title pf-m-2xl" id="modal-title">${gettext("Outpost Deployment Info")}</h1>
|
||||
</div>
|
||||
<div class="pf-c-modal-box__body" id="modal-description">
|
||||
<p><a href="https://goauthentik.io/docs/outposts/outposts/#deploy">${gettext("View deployment documentation")}</a></p>
|
||||
<form class="pf-c-form">
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_HOST</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="${document.location.toString()}" />
|
||||
</div>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_TOKEN</span>
|
||||
</label>
|
||||
<div>
|
||||
<ak-token-copy-button identifier="${item.token_identifier}">
|
||||
${gettext("Click to copy token")}
|
||||
</ak-token-copy-button>
|
||||
</div>
|
||||
</div>
|
||||
<h3>${gettext("If your authentik Instance is using a self-signed certificate, set this value.")}</h3>
|
||||
<div class="pf-c-form__group">
|
||||
<label class="pf-c-form__label" for="help-text-simple-form-name">
|
||||
<span class="pf-c-form__label-text">AUTHENTIK_INSECURE</span>
|
||||
</label>
|
||||
<input class="pf-c-form-control" readonly type="text" value="true" />
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<footer class="pf-c-modal-box__footer pf-m-align-left">
|
||||
<a class="pf-c-button pf-m-primary">${gettext("Close")}</a>
|
||||
</footer>
|
||||
</div>
|
||||
</ak-modal-button>`,
|
||||
];
|
||||
}
|
||||
|
||||
renderToolbar(): TemplateResult {
|
||||
return html`
|
||||
<ak-modal-button href=${Outpost.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()}
|
||||
`;
|
||||
}
|
||||
}
|
||||
@ -14,6 +14,7 @@ import "./pages/events/RuleListPage";
|
||||
import "./pages/providers/ProviderListPage";
|
||||
import "./pages/providers/ProviderViewPage";
|
||||
import "./pages/property-mappings/PropertyMappingListPage";
|
||||
import "./pages/outposts/OutpostListPage";
|
||||
|
||||
export const ROUTES: Route[] = [
|
||||
// Prevent infinite Shell loops
|
||||
@ -42,4 +43,5 @@ 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>`),
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user