web/admin: show connected services on user view page, fix styling (#8416)
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
21
web/src/elements/user/sources/BaseUserSettings.ts
Normal file
21
web/src/elements/user/sources/BaseUserSettings.ts
Normal file
@ -0,0 +1,21 @@
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
|
||||
import { CSSResult } from "lit";
|
||||
import { property } from "lit/decorators.js";
|
||||
|
||||
import PFButton from "@patternfly/patternfly/components/Button/button.css";
|
||||
import PFForm from "@patternfly/patternfly/components/Form/form.css";
|
||||
import PFFormControl from "@patternfly/patternfly/components/FormControl/form-control.css";
|
||||
import PFBase from "@patternfly/patternfly/patternfly-base.css";
|
||||
|
||||
export abstract class BaseUserSettings extends AKElement {
|
||||
@property()
|
||||
objectId!: string;
|
||||
|
||||
@property()
|
||||
configureUrl?: string;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [PFBase, PFButton, PFForm, PFFormControl];
|
||||
}
|
||||
}
|
||||
146
web/src/elements/user/sources/SourceSettings.ts
Normal file
146
web/src/elements/user/sources/SourceSettings.ts
Normal file
@ -0,0 +1,146 @@
|
||||
import { renderSourceIcon } from "@goauthentik/app/admin/sources/utils";
|
||||
import "@goauthentik/app/elements/user/sources/SourceSettingsOAuth";
|
||||
import "@goauthentik/app/elements/user/sources/SourceSettingsPlex";
|
||||
import "@goauthentik/app/elements/user/sources/SourceSettingsSAML";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { AKElement } from "@goauthentik/elements/Base";
|
||||
import "@goauthentik/elements/EmptyState";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { CSSResult, TemplateResult, css, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import PFDataList from "@patternfly/patternfly/components/DataList/data-list.css";
|
||||
|
||||
import { PaginatedUserSourceConnectionList, SourcesApi, UserSetting } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-settings-source")
|
||||
export class UserSourceSettingsPage extends AKElement {
|
||||
@property({ attribute: false })
|
||||
sourceSettings?: UserSetting[];
|
||||
|
||||
@property({ attribute: false })
|
||||
connections?: PaginatedUserSourceConnectionList;
|
||||
|
||||
@property({ type: Number })
|
||||
userId?: number;
|
||||
|
||||
@property({ type: Boolean })
|
||||
canConnect = true;
|
||||
|
||||
static get styles(): CSSResult[] {
|
||||
return [
|
||||
PFDataList,
|
||||
css`
|
||||
.pf-c-data-list__cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.pf-c-data-list__cell img {
|
||||
max-width: 48px;
|
||||
width: 48px;
|
||||
margin-right: 16px;
|
||||
}
|
||||
:host([theme="dark"]) .pf-c-data-list__cell img {
|
||||
filter: invert(1);
|
||||
}
|
||||
.pf-c-data-list__item {
|
||||
background-color: transparent;
|
||||
}
|
||||
`,
|
||||
];
|
||||
}
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
this.addEventListener(EVENT_REFRESH, () => {
|
||||
this.firstUpdated();
|
||||
});
|
||||
}
|
||||
|
||||
async firstUpdated(): Promise<void> {
|
||||
this.sourceSettings = await new SourcesApi(DEFAULT_CONFIG).sourcesAllUserSettingsList();
|
||||
this.connections = await new SourcesApi(DEFAULT_CONFIG).sourcesUserConnectionsAllList({
|
||||
user: this.userId,
|
||||
});
|
||||
}
|
||||
|
||||
renderSourceSettings(source: UserSetting): TemplateResult {
|
||||
let connectionPk = -1;
|
||||
if (this.connections) {
|
||||
const connections = this.connections.results.filter(
|
||||
(con) => con.source.slug === source.objectUid,
|
||||
);
|
||||
if (connections.length > 0) {
|
||||
connectionPk = connections[0].pk;
|
||||
} else {
|
||||
connectionPk = 0;
|
||||
}
|
||||
}
|
||||
switch (source.component) {
|
||||
case "ak-user-settings-source-oauth":
|
||||
return html`<ak-user-settings-source-oauth
|
||||
objectId=${source.objectUid}
|
||||
title=${source.title}
|
||||
connectionPk=${connectionPk}
|
||||
.configureUrl=${this.canConnect ? source.configureUrl : undefined}
|
||||
>
|
||||
</ak-user-settings-source-oauth>`;
|
||||
case "ak-user-settings-source-plex":
|
||||
return html`<ak-user-settings-source-plex
|
||||
objectId=${source.objectUid}
|
||||
title=${source.title}
|
||||
connectionPk=${connectionPk}
|
||||
.configureUrl=${this.canConnect ? source.configureUrl : undefined}
|
||||
>
|
||||
</ak-user-settings-source-plex>`;
|
||||
case "ak-user-settings-source-saml":
|
||||
return html`<ak-user-settings-source-saml
|
||||
objectId=${source.objectUid}
|
||||
title=${source.title}
|
||||
connectionPk=${connectionPk}
|
||||
.configureUrl=${this.canConnect ? source.configureUrl : undefined}
|
||||
>
|
||||
</ak-user-settings-source-saml>`;
|
||||
default:
|
||||
return html`<p>
|
||||
${msg(str`Error: unsupported source settings: ${source.component}`)}
|
||||
</p>`;
|
||||
}
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
return html` <ul class="pf-c-data-list" role="list">
|
||||
${this.sourceSettings
|
||||
? html`
|
||||
${this.sourceSettings.length < 1
|
||||
? html`<ak-empty-state
|
||||
header=${msg("No services available.")}
|
||||
></ak-empty-state>`
|
||||
: html`
|
||||
${this.sourceSettings.map((source) => {
|
||||
return html`<li class="pf-c-data-list__item">
|
||||
<div class="pf-c-data-list__item-row">
|
||||
<div class="pf-c-data-list__item-content">
|
||||
<div class="pf-c-data-list__cell">
|
||||
${renderSourceIcon(
|
||||
source.title,
|
||||
source.iconUrl,
|
||||
)}
|
||||
${source.title}
|
||||
</div>
|
||||
<div class="pf-c-data-list__cell">
|
||||
${this.renderSourceSettings(source)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>`;
|
||||
})}
|
||||
`}
|
||||
`
|
||||
: html`<ak-empty-state ?loading="${true}" header=${msg("Loading")}>
|
||||
</ak-empty-state>`}
|
||||
</ul>`;
|
||||
}
|
||||
}
|
||||
71
web/src/elements/user/sources/SourceSettingsOAuth.ts
Normal file
71
web/src/elements/user/sources/SourceSettingsOAuth.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { BaseUserSettings } from "@goauthentik/app/elements/user/sources/BaseUserSettings";
|
||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import "@goauthentik/elements/Spinner";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { SourcesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-settings-source-oauth")
|
||||
export class SourceSettingsOAuth extends BaseUserSettings {
|
||||
@property()
|
||||
title!: string;
|
||||
|
||||
@property({ type: Number })
|
||||
connectionPk = 0;
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.connectionPk === -1) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.connectionPk > 0) {
|
||||
return html`<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesUserConnectionsOauthDestroy({
|
||||
id: this.connectionPk,
|
||||
})
|
||||
.then(() => {
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: msg("Successfully disconnected source"),
|
||||
});
|
||||
})
|
||||
.catch((exc) => {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg(str`Failed to disconnected source: ${exc}`),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.parentElement?.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Disconnect")}
|
||||
</button>`;
|
||||
}
|
||||
if (this.configureUrl) {
|
||||
return html`<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
href="${this.configureUrl}${AndNext(
|
||||
`/if/user/#/settings;${JSON.stringify({ page: "page-sources" })}`,
|
||||
)}"
|
||||
>
|
||||
${msg("Connect")}
|
||||
</a>`;
|
||||
}
|
||||
return html`${msg("-")}`;
|
||||
}
|
||||
}
|
||||
87
web/src/elements/user/sources/SourceSettingsPlex.ts
Normal file
87
web/src/elements/user/sources/SourceSettingsPlex.ts
Normal file
@ -0,0 +1,87 @@
|
||||
import { BaseUserSettings } from "@goauthentik/app/elements/user/sources/BaseUserSettings";
|
||||
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { PlexAPIClient, popupCenterScreen } from "@goauthentik/common/helpers/plex";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import "@goauthentik/elements/Spinner";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { SourcesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-settings-source-plex")
|
||||
export class SourceSettingsPlex extends BaseUserSettings {
|
||||
@property()
|
||||
title!: string;
|
||||
|
||||
@property({ type: Number })
|
||||
connectionPk = 0;
|
||||
|
||||
async doPlex(): Promise<void> {
|
||||
const authInfo = await PlexAPIClient.getPin(this.configureUrl || "");
|
||||
const authWindow = await popupCenterScreen(authInfo.authUrl, "plex auth", 550, 700);
|
||||
PlexAPIClient.pinPoll(this.configureUrl || "", authInfo.pin.id).then((token) => {
|
||||
authWindow?.close();
|
||||
new SourcesApi(DEFAULT_CONFIG).sourcesPlexRedeemTokenAuthenticatedCreate({
|
||||
plexTokenRedeemRequest: {
|
||||
plexToken: token,
|
||||
},
|
||||
slug: this.objectId,
|
||||
});
|
||||
});
|
||||
this.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.connectionPk === -1) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.connectionPk > 0) {
|
||||
return html`<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesUserConnectionsPlexDestroy({
|
||||
id: this.connectionPk,
|
||||
})
|
||||
.then(() => {
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: msg("Successfully disconnected source"),
|
||||
});
|
||||
})
|
||||
.catch((exc) => {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg(str`Failed to disconnected source: ${exc}`),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.parentElement?.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Disconnect")}
|
||||
</button>`;
|
||||
}
|
||||
if (this.configureUrl) {
|
||||
return html`<button @click=${this.doPlex} class="pf-c-button pf-m-primary">
|
||||
${msg("Connect")}
|
||||
</button>`;
|
||||
}
|
||||
return html`${msg("-")}`;
|
||||
}
|
||||
}
|
||||
71
web/src/elements/user/sources/SourceSettingsSAML.ts
Normal file
71
web/src/elements/user/sources/SourceSettingsSAML.ts
Normal file
@ -0,0 +1,71 @@
|
||||
import { BaseUserSettings } from "@goauthentik/app/elements/user/sources/BaseUserSettings";
|
||||
import { AndNext, DEFAULT_CONFIG } from "@goauthentik/common/api/config";
|
||||
import { EVENT_REFRESH } from "@goauthentik/common/constants";
|
||||
import { MessageLevel } from "@goauthentik/common/messages";
|
||||
import "@goauthentik/elements/Spinner";
|
||||
import { showMessage } from "@goauthentik/elements/messages/MessageContainer";
|
||||
|
||||
import { msg, str } from "@lit/localize";
|
||||
import { TemplateResult, html } from "lit";
|
||||
import { customElement, property } from "lit/decorators.js";
|
||||
|
||||
import { SourcesApi } from "@goauthentik/api";
|
||||
|
||||
@customElement("ak-user-settings-source-saml")
|
||||
export class SourceSettingsSAML extends BaseUserSettings {
|
||||
@property()
|
||||
title!: string;
|
||||
|
||||
@property({ type: Number })
|
||||
connectionPk = 0;
|
||||
|
||||
render(): TemplateResult {
|
||||
if (this.connectionPk === -1) {
|
||||
return html`<ak-spinner></ak-spinner>`;
|
||||
}
|
||||
if (this.connectionPk > 0) {
|
||||
return html`<button
|
||||
class="pf-c-button pf-m-danger"
|
||||
@click=${() => {
|
||||
return new SourcesApi(DEFAULT_CONFIG)
|
||||
.sourcesUserConnectionsSamlDestroy({
|
||||
id: this.connectionPk,
|
||||
})
|
||||
.then(() => {
|
||||
showMessage({
|
||||
level: MessageLevel.info,
|
||||
message: msg("Successfully disconnected source"),
|
||||
});
|
||||
})
|
||||
.catch((exc) => {
|
||||
showMessage({
|
||||
level: MessageLevel.error,
|
||||
message: msg(str`Failed to disconnected source: ${exc}`),
|
||||
});
|
||||
})
|
||||
.finally(() => {
|
||||
this.parentElement?.dispatchEvent(
|
||||
new CustomEvent(EVENT_REFRESH, {
|
||||
bubbles: true,
|
||||
composed: true,
|
||||
}),
|
||||
);
|
||||
});
|
||||
}}
|
||||
>
|
||||
${msg("Disconnect")}
|
||||
</button>`;
|
||||
}
|
||||
if (this.configureUrl) {
|
||||
return html`<a
|
||||
class="pf-c-button pf-m-primary"
|
||||
href="${this.configureUrl}${AndNext(
|
||||
`/if/user/#/settings;${JSON.stringify({ page: "page-sources" })}`,
|
||||
)}"
|
||||
>
|
||||
${msg("Connect")}
|
||||
</a>`;
|
||||
}
|
||||
return html`${msg("-")}`;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user