enterprise/rac: Improve client connection status & bugfixes (cherry-pick #12684) (#12727)

enterprise/rac: Improve client connection status & bugfixes (#12684)

* enterprise/rac: improve status message when connecting/connection failed



* set fixed DPI



* automatically set resize method for RDP



---------

Signed-off-by: Jens Langhammer <jens@goauthentik.io>
Co-authored-by: Jens L. <jens@goauthentik.io>
This commit is contained in:
gcp-cherry-pick-bot[bot]
2025-01-17 18:27:31 +01:00
committed by GitHub
parent 9d81f0598c
commit 01959132e8
4 changed files with 76 additions and 22 deletions

View File

@ -159,9 +159,9 @@ class ConnectionToken(ExpiringModel):
default_settings["port"] = str(port) default_settings["port"] = str(port)
else: else:
default_settings["hostname"] = self.endpoint.host default_settings["hostname"] = self.endpoint.host
default_settings["client-name"] = "authentik" if self.endpoint.protocol == Protocols.RDP:
# default_settings["enable-drive"] = "true" default_settings["resize-method"] = "display-update"
# default_settings["drive-name"] = "authentik" default_settings["client-name"] = f"authentik - {self.session.user}"
settings = {} settings = {}
always_merger.merge(settings, default_settings) always_merger.merge(settings, default_settings)
always_merger.merge(settings, self.endpoint.provider.settings) always_merger.merge(settings, self.endpoint.provider.settings)

View File

@ -50,9 +50,10 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"resize-method": "display-update",
}, },
) )
# Set settings in provider # Set settings in provider
@ -63,10 +64,11 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"level": "provider", "level": "provider",
"resize-method": "display-update",
}, },
) )
# Set settings in endpoint # Set settings in endpoint
@ -79,10 +81,11 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"level": "endpoint", "level": "endpoint",
"resize-method": "display-update",
}, },
) )
# Set settings in token # Set settings in token
@ -95,10 +98,11 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"level": "token", "level": "token",
"resize-method": "display-update",
}, },
) )
# Set settings in property mapping (provider) # Set settings in property mapping (provider)
@ -114,10 +118,11 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"level": "property_mapping_provider", "level": "property_mapping_provider",
"resize-method": "display-update",
}, },
) )
# Set settings in property mapping (endpoint) # Set settings in property mapping (endpoint)
@ -135,11 +140,12 @@ class TestModels(TransactionTestCase):
{ {
"hostname": self.endpoint.host.split(":")[0], "hostname": self.endpoint.host.split(":")[0],
"port": "1324", "port": "1324",
"client-name": "authentik", "client-name": f"authentik - {self.user}",
"drive-path": path, "drive-path": path,
"create-drive-path": "true", "create-drive-path": "true",
"level": "property_mapping_endpoint", "level": "property_mapping_endpoint",
"foo": "true", "foo": "true",
"bar": "6", "bar": "6",
"resize-method": "display-update",
}, },
) )

View File

@ -18,6 +18,12 @@ export class LoadingOverlay extends AKElement implements ILoadingOverlay {
@property({ type: Boolean, attribute: "topmost" }) @property({ type: Boolean, attribute: "topmost" })
topmost = false; topmost = false;
@property({ type: Boolean })
loading = true;
@property({ type: String })
icon = "";
static get styles() { static get styles() {
return [ return [
PFBase, PFBase,
@ -40,7 +46,7 @@ export class LoadingOverlay extends AKElement implements ILoadingOverlay {
} }
render() { render() {
return html`<ak-empty-state loading header=""> return html`<ak-empty-state ?loading=${this.loading} header="" icon=${this.icon}>
<span slot="body"><slot></slot></span> <span slot="body"><slot></slot></span>
</ak-empty-state>`; </ak-empty-state>`;
} }

View File

@ -4,7 +4,7 @@ import "@goauthentik/elements/LoadingOverlay";
import Guacamole from "guacamole-common-js"; import Guacamole from "guacamole-common-js";
import { msg, str } from "@lit/localize"; 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 { customElement, property, state } from "lit/decorators.js";
import AKGlobal from "@goauthentik/common/styles/authentik.css"; import AKGlobal from "@goauthentik/common/styles/authentik.css";
@ -21,6 +21,23 @@ enum GuacClientState {
DISCONNECTED = 5, 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 AUDIO_INPUT_MIMETYPE = "audio/L16;rate=44100,channels=2";
const RECONNECT_ATTEMPTS_INITIAL = 5; const RECONNECT_ATTEMPTS_INITIAL = 5;
const RECONNECT_ATTEMPTS = 5; const RECONNECT_ATTEMPTS = 5;
@ -64,6 +81,9 @@ export class RacInterface extends Interface {
@state() @state()
clientState?: GuacClientState; clientState?: GuacClientState;
@state()
clientStatus?: Guacamole.Status;
@state() @state()
reconnectingMessage = ""; reconnectingMessage = "";
@ -138,6 +158,7 @@ export class RacInterface extends Interface {
}; };
this.client = new Guacamole.Client(this.tunnel); this.client = new Guacamole.Client(this.tunnel);
this.client.onerror = (err) => { this.client.onerror = (err) => {
this.clientStatus = err;
console.debug("authentik/rac: error: ", err); console.debug("authentik/rac: error: ", err);
this.reconnect(); this.reconnect();
}; };
@ -175,6 +196,10 @@ export class RacInterface extends Interface {
const params = new URLSearchParams(); const params = new URLSearchParams();
params.set("screen_width", Math.floor(RacInterface.domSize().width).toString()); params.set("screen_width", Math.floor(RacInterface.domSize().width).toString());
params.set("screen_height", Math.floor(RacInterface.domSize().height).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()); this.client.connect(params.toString());
} }
@ -221,6 +246,7 @@ export class RacInterface extends Interface {
return; return;
} }
this.hasConnected = true; this.hasConnected = true;
this.clientStatus = undefined;
this.container = this.client.getDisplay().getElement(); this.container = this.client.getDisplay().getElement();
this.initMouse(this.container); this.initMouse(this.container);
this.client?.sendSize( this.client?.sendSize(
@ -310,19 +336,35 @@ export class RacInterface extends Interface {
console.debug("authentik/rac: Sent clipboard"); 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}<br />${this.clientStatus.message}`;
}
const isLoading = [
GuacClientState.CONNECTING,
GuacClientState.DISCONNECTING,
GuacClientState.WAITING,
].includes(this.clientState);
return html`
<ak-loading-overlay ?loading=${isLoading} icon="fa fa-times">
<span> ${message} </span>
</ak-loading-overlay>
`;
}
render(): TemplateResult { render(): TemplateResult {
return html` return html`
${this.clientState !== GuacClientState.CONNECTED ${this.renderOverlay()}
? html`
<ak-loading-overlay>
<span>
${this.hasConnected
? html`${this.reconnectingMessage}`
: html`${msg("Connecting...")}`}
</span>
</ak-loading-overlay>
`
: html``}
<div class="container">${this.container}</div> <div class="container">${this.container}</div>
`; `;
} }