enterprise/providers/rac: connection token management (#8467)

This commit is contained in:
Jens L
2024-02-14 18:57:11 +01:00
committed by GitHub
parent c048f4a356
commit 4733778460
23 changed files with 846 additions and 98 deletions

View File

@ -1,3 +1,4 @@
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/app/common/constants";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/HorizontalFormElement";
@ -34,14 +35,19 @@ export class EnterpriseLicenseForm extends ModelForm<License, string> {
}
async send(data: License): Promise<License> {
return this.instance
? new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicensePartialUpdate({
licenseUuid: this.instance.licenseUuid || "",
patchedLicenseRequest: data,
})
: new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseCreate({
licenseRequest: data,
});
return (
this.instance
? new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicensePartialUpdate({
licenseUuid: this.instance.licenseUuid || "",
patchedLicenseRequest: data,
})
: new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseCreate({
licenseRequest: data,
})
).then((data) => {
window.dispatchEvent(new CustomEvent(EVENT_REFRESH_ENTERPRISE));
return data;
});
}
renderForm(): TemplateResult {

View File

@ -1,4 +1,3 @@
import { first } from "@goauthentik/app/common/utils";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { docLink } from "@goauthentik/common/global";
import "@goauthentik/elements/CodeMirror";
@ -6,6 +5,8 @@ import { CodeMirrorMode } from "@goauthentik/elements/CodeMirror";
import "@goauthentik/elements/forms/FormGroup";
import "@goauthentik/elements/forms/HorizontalFormElement";
import { ModelForm } from "@goauthentik/elements/forms/ModelForm";
import "@goauthentik/elements/forms/Radio";
import type { RadioOption } from "@goauthentik/elements/forms/Radio";
import { msg } from "@lit/localize";
import { TemplateResult, html } from "lit";
@ -14,6 +15,23 @@ import { ifDefined } from "lit/directives/if-defined.js";
import { PropertymappingsApi, RACPropertyMapping } from "@goauthentik/api";
export const staticSettingOptions: RadioOption<string | undefined>[] = [
{
label: msg("Unconfigured"),
value: undefined,
default: true,
description: html`${msg("This option will not be changed by this mapping.")}`,
},
{
label: msg("Enabled"),
value: "true",
},
{
label: msg("Disabled"),
value: "false",
},
];
@customElement("ak-property-mapping-rac-form")
export class PropertyMappingLDAPForm extends ModelForm<RACPropertyMapping, string> {
loadInstance(pk: string): Promise<RACPropertyMapping> {
@ -58,7 +76,6 @@ export class PropertyMappingLDAPForm extends ModelForm<RACPropertyMapping, strin
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal
label=${msg("Username")}
?required=${true}
name="staticSettings.username"
>
<input
@ -70,7 +87,6 @@ export class PropertyMappingLDAPForm extends ModelForm<RACPropertyMapping, strin
</ak-form-element-horizontal>
<ak-form-element-horizontal
label=${msg("Password")}
?required=${true}
name="staticSettings.password"
>
<input
@ -85,81 +101,45 @@ export class PropertyMappingLDAPForm extends ModelForm<RACPropertyMapping, strin
<ak-form-group>
<span slot="header"> ${msg("RDP settings")} </span>
<div slot="body" class="pf-c-form">
<ak-form-element-horizontal name="staticSettings.ignore-cert">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(
this.instance?.staticSettings["ignore-cert"],
false,
)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label"
>${msg("Ignore server certificate")}</span
>
</label>
<ak-form-element-horizontal
label=${msg("Ignore server certificate")}
name="staticSettings.ignore-cert"
>
<ak-radio
.options=${staticSettingOptions}
.value=${this.instance?.staticSettings["ignore-cert"]}
>
</ak-radio>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="staticSettings.enable-wallpaper">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(
this.instance?.staticSettings["enable-wallpaper"],
false,
)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Enable wallpaper")}</span>
</label>
<ak-form-element-horizontal
label=${msg("Enable wallpaper")}
name="staticSettings.enable-wallpaper"
>
<ak-radio
.options=${staticSettingOptions}
.value=${this.instance?.staticSettings["enable-wallpaper"]}
>
</ak-radio>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="staticSettings.enable-font-smoothing">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(
this.instance?.staticSettings["enable-font-smoothing"],
false,
)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label">${msg("Enable font-smoothing")}</span>
</label>
<ak-form-element-horizontal
label=${msg("Enable font-smoothing")}
name="staticSettings.enable-font-smoothing"
>
<ak-radio
.options=${staticSettingOptions}
.value=${this.instance?.staticSettings["enable-font-smoothing"]}
>
</ak-radio>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="staticSettings.enable-full-window-drag">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(
this.instance?.staticSettings["enable-full-window-drag"],
false,
)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label"
>${msg("Enable full window dragging")}</span
>
</label>
<ak-form-element-horizontal
label=${msg("Enable full window dragging")}
name="staticSettings.enable-full-window-drag"
>
<ak-radio
.options=${staticSettingOptions}
.value=${this.instance?.staticSettings["enable-full-window-drag"]}
>
</ak-radio>
</ak-form-element-horizontal>
</div>
</ak-form-group>

View File

@ -0,0 +1,98 @@
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { uiConfig } from "@goauthentik/common/ui/config";
import "@goauthentik/elements/buttons/SpinnerButton";
import "@goauthentik/elements/forms/DeleteBulkForm";
import "@goauthentik/elements/forms/ModalForm";
import { PaginatedResponse, Table } from "@goauthentik/elements/table/Table";
import { TableColumn } from "@goauthentik/elements/table/Table";
import "@patternfly/elements/pf-tooltip/pf-tooltip.js";
import { msg } from "@lit/localize";
import { CSSResult, TemplateResult, html } from "lit";
import { customElement, property } from "lit/decorators.js";
import PFDescriptionList from "@patternfly/patternfly/components/DescriptionList/description-list.css";
import { ConnectionToken, Endpoint, RACProvider, RacApi } from "@goauthentik/api";
@customElement("ak-rac-connection-token-list")
export class ConnectionTokenListPage extends Table<ConnectionToken> {
checkbox = true;
clearOnRefresh = true;
searchEnabled(): boolean {
return true;
}
@property()
order = "name";
@property({ attribute: false })
provider?: RACProvider;
@property({ type: Number })
userId?: number;
static get styles(): CSSResult[] {
return super.styles.concat(PFDescriptionList);
}
async apiEndpoint(page: number): Promise<PaginatedResponse<ConnectionToken>> {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensList({
ordering: this.order,
page: page,
pageSize: (await uiConfig()).pagination.perPage,
search: this.search || "",
provider: this.provider?.pk,
sessionUser: this.userId,
});
}
renderToolbarSelected(): TemplateResult {
const disabled = this.selectedElements.length < 1;
return html`<ak-forms-delete-bulk
objectLabel=${msg("Connection Token(s)")}
.objects=${this.selectedElements}
.metadata=${(item: Endpoint) => {
return [
{ key: msg("Name"), value: item.name },
{ key: msg("Host"), value: item.host },
];
}}
.usedBy=${(item: Endpoint) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensUsedByList({
connectionTokenUuid: item.pk,
});
}}
.delete=${(item: Endpoint) => {
return new RacApi(DEFAULT_CONFIG).racConnectionTokensDestroy({
connectionTokenUuid: item.pk,
});
}}
>
<button ?disabled=${disabled} slot="trigger" class="pf-c-button pf-m-danger">
${msg("Delete")}
</button>
</ak-forms-delete-bulk>`;
}
columns(): TableColumn[] {
if (this.provider) {
return [
new TableColumn(msg("Endpoint"), "endpoint__name"),
new TableColumn(msg("User"), "session__user"),
];
}
return [
new TableColumn(msg("Provider"), "provider__name"),
new TableColumn(msg("Endpoint"), "endpoint__name"),
];
}
row(item: ConnectionToken): TemplateResult[] {
if (this.provider) {
return [html`${item.endpointObj.name}`, html`${item.user.username}`];
}
return [html`${item.providerObj.name}`, html`${item.endpointObj.name}`];
}
}

View File

@ -107,6 +107,28 @@ export class RACProviderFormPage extends ModelForm<RACProvider, number> {
</p>
<ak-utils-time-delta-help></ak-utils-time-delta-help>
</ak-form-element-horizontal>
<ak-form-element-horizontal name="deleteTokenOnDisconnect">
<label class="pf-c-switch">
<input
class="pf-c-switch__input"
type="checkbox"
?checked=${first(this.instance?.deleteTokenOnDisconnect, false)}
/>
<span class="pf-c-switch__toggle">
<span class="pf-c-switch__toggle-icon">
<i class="fas fa-check" aria-hidden="true"></i>
</span>
</span>
<span class="pf-c-switch__label"
>${msg("Delete authorization on disconnect")}</span
>
</label>
<p class="pf-c-form__helper-text">
${msg(
"When enabled, connection authorizations will be deleted when a client disconnects. This will force clients with flaky internet connections to re-authorize the endpoint.",
)}
</p>
</ak-form-element-horizontal>
<ak-form-group .expanded=${true}>
<span slot="header"> ${msg("Protocol settings")} </span>

View File

@ -1,4 +1,5 @@
import "@goauthentik/admin/providers/RelatedApplicationButton";
import "@goauthentik/admin/providers/rac/ConnectionTokenList";
import "@goauthentik/admin/providers/rac/EndpointForm";
import "@goauthentik/admin/providers/rac/EndpointList";
import "@goauthentik/admin/providers/rac/RACProviderForm";
@ -86,6 +87,15 @@ export class RACProviderViewPage extends AKElement {
<section slot="page-overview" data-tab-title="${msg("Overview")}">
${this.renderTabOverview()}
</section>
<section slot="page-connections" data-tab-title="${msg("Connections")}">
<div class="pf-c-card">
<div class="pf-c-card__body">
<ak-rac-connection-token-list
.provider=${this.provider}
></ak-rac-connection-token-list>
</div>
</div>
</section>
<section
slot="page-changelog"
data-tab-title="${msg("Changelog")}"

View File

@ -1,4 +1,5 @@
import "@goauthentik/admin/groups/RelatedGroupList";
import "@goauthentik/admin/providers/rac/ConnectionTokenList";
import "@goauthentik/admin/users/UserActiveForm";
import "@goauthentik/admin/users/UserApplicationTable";
import "@goauthentik/admin/users/UserChart";
@ -329,6 +330,16 @@ export class UserViewPage extends WithCapabilitiesConfig(AKElement) {
</ak-user-settings-source>
</div>
</section>
<section
slot="page-rac-connection-tokens"
data-tab-title="${msg("RAC Connections")}"
class="pf-c-page__main-section pf-m-no-padding-mobile"
>
<div class="pf-c-card">
<ak-rac-connection-token-list userId=${user.pk}>
</ak-rac-connection-token-list>
</div>
</section>
</ak-tabs>
`;
}

View File

@ -19,6 +19,7 @@ export const EVENT_LOCALE_REQUEST = "ak-locale-request";
export const EVENT_REQUEST_POST = "ak-request-post";
export const EVENT_MESSAGE = "ak-message";
export const EVENT_THEME_CHANGE = "ak-theme-change";
export const EVENT_REFRESH_ENTERPRISE = "ak-refresh-enterprise";
export const WS_MSG_TYPE_MESSAGE = "message";
export const WS_MSG_TYPE_REFRESH = "refresh";

View File

@ -1,3 +1,4 @@
import { EVENT_REFRESH_ENTERPRISE } from "@goauthentik/app/common/constants";
import { DEFAULT_CONFIG } from "@goauthentik/common/api/config";
import { brand, config } from "@goauthentik/common/api/config";
import { UIConfig, uiConfig } from "@goauthentik/common/ui/config";
@ -109,8 +110,16 @@ export class EnterpriseAwareInterface extends Interface {
constructor() {
super();
new EnterpriseApi(DEFAULT_CONFIG).enterpriseLicenseSummaryRetrieve().then((enterprise) => {
this.licenseSummary = enterprise;
const refreshStatus = () => {
new EnterpriseApi(DEFAULT_CONFIG)
.enterpriseLicenseSummaryRetrieve()
.then((enterprise) => {
this.licenseSummary = enterprise;
});
};
refreshStatus();
window.addEventListener(EVENT_REFRESH_ENTERPRISE, () => {
refreshStatus();
});
}
}

View File

@ -14,7 +14,7 @@ import { randomId } from "../utils/randomId";
export interface RadioOption<T> {
label: string;
description?: TemplateResult;
default: boolean;
default?: boolean;
value: T;
}