diff --git a/client/uis/screens/lockscreen.ts b/client/uis/screens/lockscreen.ts index 4d57dea..2cc64c1 100644 --- a/client/uis/screens/lockscreen.ts +++ b/client/uis/screens/lockscreen.ts @@ -1,13 +1,15 @@ import { Authenticator, AuthenticatorEvents } from "../../auth"; -import { LightDMUser } from "nody-greeter-types"; +import { LightDMUser, ThemeUtils } from "nody-greeter-types"; import { UIScreen, UILockScreenElements } from "../screen"; import { UI } from "../../ui"; +const PATH_LOCK_TIMESTAMP_PREFIX = '/tmp/codam_web_greeter_lock_timestamp'; + export class LockScreenUI extends UIScreen { public readonly _form: UILockScreenElements; private readonly _activeSession: LightDMUser; private _isExamMode: boolean = false; - private _lockedTime: Date = new Date(); + private _lockedTime: Date | null = null; protected _events: AuthenticatorEvents = { authenticationStart: () => { this._disableForm(); @@ -43,6 +45,10 @@ export class LockScreenUI extends UIScreen { } as UILockScreenElements; this._initForm(); + + // Check when the screen was locked every minute (delete the lock_timestamp file in /tmp to prevent the automated logout) + setInterval(this._getAndSetLockedTimestamp.bind(this), 60000); + this._getAndSetLockedTimestamp(); } protected _initForm(): void { @@ -122,16 +128,53 @@ export class LockScreenUI extends UIScreen { return (this._form as UILockScreenElements).passwordInput; } + public get lockedTime(): Date | null { + return this._lockedTime; + } + + private _getScreenLockedTimestamp(login: string): Promise { + return new Promise((resolve, reject) => { + fetch(`${PATH_LOCK_TIMESTAMP_PREFIX}_${login}`) + .then(response => response.text()) + .then(text => { + // Get the first word from the text file + const timestamp = text.split(' ')[0]; + resolve(new Date(parseInt(timestamp) * 1000)); + }) + .catch(() => { + reject(); + }); + }); + } + + private _getAndSetLockedTimestamp(): void { + this._getScreenLockedTimestamp(this._activeSession.username) + .then((timestamp: Date) => { + this._lockedTime = timestamp; + this._lockedTimer(); // run once immediately, after this the interval will take care of updating the timer + }) + .catch(() => { + // Unable to get the screen locked timestamp, prevent automated logout by setting the locked time to null + this._lockedTime = null; + }); + } + private _lockedTimer(): void { + if (!this._lockedTime) { + // Unsure when the screen was locked, no automated logout possible + return; + } + const logoutAfter = 42; // minutes const lockedMinutesAgo = (Date.now() - this._lockedTime.getTime()) / 1000 / 60; const timeRemaining = logoutAfter - lockedMinutesAgo; if (timeRemaining <= 0.25) { this._disableForm(); this._form.lockedTimeAgo.innerText = "Automated logout in progress..."; - if (timeRemaining < -5) { + if (timeRemaining < -5) { // Give it a 5 minute grace period // Add debug text indicating the systemd service might have failed or was not installed window.ui.setDebugInfo("Automated logout appears to take a while. Is the systemd idling service from codam-web-greeter installed and enabled?"); + this._enableForm(); // Allow the user to just unlock the screen again } } else { diff --git a/systemd/system/codam-web-greeter-idler.sh b/systemd/system/codam-web-greeter-idler.sh index 2fc6797..b5a1b66 100644 --- a/systemd/system/codam-web-greeter-idler.sh +++ b/systemd/system/codam-web-greeter-idler.sh @@ -17,13 +17,29 @@ while IFS= read -r line; do if ! [[ "$DISPLAY" =~ ^: ]]; then continue fi + # Get idle time from X-session using sudo + # This time is used to determine if the session has been idle for too long (possibly without locking the screen) IDLE_TIME=$(/usr/bin/sudo -u "$USERNAME" DISPLAY="$DISPLAY" /usr/bin/xprintidle) - # Check if session has been idle for over 42 minutes - if [ "$IDLE_TIME" -gt 2520000 ]; then - /usr/bin/echo "Session for user $USERNAME has been idle for over 42 minutes (idletime $IDLE_TIME ms), forcing logout now by restarting lightdm" + + # Check if lock_timestamp file exists, and if so read the locked_at_timestamp + # This time is used to determine if the screen lock has been active for too long + # Sometimes xprintidle doesn't work properly when the screen is locked due to programs running in the user session in the background + TIME_SINCE_LOCK=$((0)) # Placeholder + if [ -f "/tmp/codam_web_greeter_lock_timestamp_$USERNAME" ]; then + # Get the locked_at_timestamp from the file + LOCKED_AT_TIMESTAMP=$(/usr/bin/awk '{print $1}' "/tmp/codam_web_greeter_lock_timestamp_$USERNAME") + # Calculate the time since the session was locked + TIME_SINCE_LOCK=$((($(date +%s) - LOCKED_AT_TIMESTAMP) * 1000)) + fi + + # Check if session has been idle for long enough + MAX_IDLE_TIME_MINUTES=$((42)) + MAX_IDLE_TIME=$((MAX_IDLE_TIME_MINUTES * 60 * 1000)) + if [ "$IDLE_TIME" -gt "$MAX_IDLE_TIME" ] || [ "$TIME_SINCE_LOCK" -gt "$MAX_IDLE_TIME" ]; then + /usr/bin/echo "Session for user $USERNAME has been idle for over 42 minutes (idletime $IDLE_TIME ms, time_since_lock $TIME_SINCE_LOCK ms), forcing logout now by restarting lightdm" /usr/bin/systemctl restart lightdm else - /usr/bin/echo "Session for $USERNAME has been idle for $((IDLE_TIME / 1000)) seconds" + /usr/bin/echo "Session for $USERNAME has been idle for $((IDLE_TIME / 1000)) seconds, screen locked for $((TIME_SINCE_LOCK / 1000)) seconds" fi done <<< "$WHO_OUTPUT"