diff --git a/authentik/enterprise/providers/rac/models.py b/authentik/enterprise/providers/rac/models.py index ca54a55b04..6ee18ba1e3 100644 --- a/authentik/enterprise/providers/rac/models.py +++ b/authentik/enterprise/providers/rac/models.py @@ -159,9 +159,9 @@ class ConnectionToken(ExpiringModel): default_settings["port"] = str(port) else: default_settings["hostname"] = self.endpoint.host - default_settings["client-name"] = "authentik" - # default_settings["enable-drive"] = "true" - # default_settings["drive-name"] = "authentik" + if self.endpoint.protocol == Protocols.RDP: + default_settings["resize-method"] = "display-update" + default_settings["client-name"] = f"authentik - {self.session.user}" settings = {} always_merger.merge(settings, default_settings) always_merger.merge(settings, self.endpoint.provider.settings) diff --git a/authentik/enterprise/providers/rac/tests/test_models.py b/authentik/enterprise/providers/rac/tests/test_models.py index 5e583d7b6d..b6e7258d83 100644 --- a/authentik/enterprise/providers/rac/tests/test_models.py +++ b/authentik/enterprise/providers/rac/tests/test_models.py @@ -50,9 +50,10 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", + "resize-method": "display-update", }, ) # Set settings in provider @@ -63,10 +64,11 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", "level": "provider", + "resize-method": "display-update", }, ) # Set settings in endpoint @@ -79,10 +81,11 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", "level": "endpoint", + "resize-method": "display-update", }, ) # Set settings in token @@ -95,10 +98,11 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", "level": "token", + "resize-method": "display-update", }, ) # Set settings in property mapping (provider) @@ -114,10 +118,11 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", "level": "property_mapping_provider", + "resize-method": "display-update", }, ) # Set settings in property mapping (endpoint) @@ -135,11 +140,12 @@ class TestModels(TransactionTestCase): { "hostname": self.endpoint.host.split(":")[0], "port": "1324", - "client-name": "authentik", + "client-name": f"authentik - {self.user}", "drive-path": path, "create-drive-path": "true", "level": "property_mapping_endpoint", "foo": "true", "bar": "6", + "resize-method": "display-update", }, ) diff --git a/web/src/elements/LoadingOverlay.ts b/web/src/elements/LoadingOverlay.ts index 7025445c7f..6291180893 100644 --- a/web/src/elements/LoadingOverlay.ts +++ b/web/src/elements/LoadingOverlay.ts @@ -18,6 +18,12 @@ export class LoadingOverlay extends AKElement implements ILoadingOverlay { @property({ type: Boolean, attribute: "topmost" }) topmost = false; + @property({ type: Boolean }) + loading = true; + + @property({ type: String }) + icon = ""; + static get styles() { return [ PFBase, @@ -40,7 +46,7 @@ export class LoadingOverlay extends AKElement implements ILoadingOverlay { } render() { - return html` + return html` `; } diff --git a/web/src/enterprise/rac/index.ts b/web/src/enterprise/rac/index.ts index 01568d6582..19089e5ab9 100644 --- a/web/src/enterprise/rac/index.ts +++ b/web/src/enterprise/rac/index.ts @@ -4,7 +4,7 @@ import "@goauthentik/elements/LoadingOverlay"; import Guacamole from "guacamole-common-js"; import { msg, str } from "@lit/localize"; -import { CSSResult, TemplateResult, css, html } from "lit"; +import { CSSResult, TemplateResult, css, html, nothing } from "lit"; import { customElement, property, state } from "lit/decorators.js"; import AKGlobal from "@goauthentik/common/styles/authentik.css"; @@ -21,6 +21,23 @@ enum GuacClientState { DISCONNECTED = 5, } +export function GuacStateToString(state: GuacClientState): string { + switch (state) { + case GuacClientState.IDLE: + return msg("Idle"); + case GuacClientState.CONNECTING: + return msg("Connecting"); + case GuacClientState.WAITING: + return msg("Waiting"); + case GuacClientState.CONNECTED: + return msg("Connected"); + case GuacClientState.DISCONNECTING: + return msg("Disconnecting"); + case GuacClientState.DISCONNECTED: + return msg("Disconnected"); + } +} + const AUDIO_INPUT_MIMETYPE = "audio/L16;rate=44100,channels=2"; const RECONNECT_ATTEMPTS_INITIAL = 5; const RECONNECT_ATTEMPTS = 5; @@ -64,6 +81,9 @@ export class RacInterface extends Interface { @state() clientState?: GuacClientState; + @state() + clientStatus?: Guacamole.Status; + @state() reconnectingMessage = ""; @@ -138,6 +158,7 @@ export class RacInterface extends Interface { }; this.client = new Guacamole.Client(this.tunnel); this.client.onerror = (err) => { + this.clientStatus = err; console.debug("authentik/rac: error: ", err); this.reconnect(); }; @@ -175,6 +196,10 @@ export class RacInterface extends Interface { const params = new URLSearchParams(); params.set("screen_width", Math.floor(RacInterface.domSize().width).toString()); params.set("screen_height", Math.floor(RacInterface.domSize().height).toString()); + // https://github.com/goauthentik/authentik/pull/11757 + // there are DPI issues when using SSH on HiDPi screens + // but if we're not setting DPI at all the resolution is not respected at all + params.set("screen_dpi", "96"); this.client.connect(params.toString()); } @@ -221,6 +246,7 @@ export class RacInterface extends Interface { return; } this.hasConnected = true; + this.clientStatus = undefined; this.container = this.client.getDisplay().getElement(); this.initMouse(this.container); this.client?.sendSize( @@ -310,19 +336,35 @@ export class RacInterface extends Interface { console.debug("authentik/rac: Sent clipboard"); } + renderOverlay() { + if (!this.clientState || this.clientState == GuacClientState.CONNECTED) { + return nothing; + } + let message = html`${GuacStateToString(this.clientState)}`; + if (this.clientState == GuacClientState.WAITING) { + message = html`${msg("Connecting...")}`; + } + if (this.hasConnected) { + message = html`${this.reconnectingMessage}`; + } + if (this.clientStatus?.message) { + message = html`${message}
${this.clientStatus.message}`; + } + const isLoading = [ + GuacClientState.CONNECTING, + GuacClientState.DISCONNECTING, + GuacClientState.WAITING, + ].includes(this.clientState); + return html` + + ${message} + + `; + } + render(): TemplateResult { return html` - ${this.clientState !== GuacClientState.CONNECTED - ? html` - - - ${this.hasConnected - ? html`${this.reconnectingMessage}` - : html`${msg("Connecting...")}`} - - - ` - : html``} + ${this.renderOverlay()}
${this.container}
`; }