From 9599102b9e43c9ab8cf4a2ed2bf635baee6b2f29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Damian=20B=C3=BCchel?= Date: Mon, 29 Jun 2020 19:29:48 +0200 Subject: [PATCH] SEBWIN-414: Implemented lock screen for user session switch. --- .../ClientControllerTests.cs | 4 + .../Operations/SystemMonitorOperationTests.cs | 53 ++++++++ .../SafeExamBrowser.Client.UnitTests.csproj | 1 + SafeExamBrowser.Client/ClientController.cs | 124 ++++++++++++------ SafeExamBrowser.Client/CompositionRoot.cs | 4 + .../Operations/SystemMonitorOperation.cs | 51 +++++++ .../SafeExamBrowser.Client.csproj | 1 + SafeExamBrowser.I18n.Contracts/TextKey.cs | 13 +- SafeExamBrowser.I18n/Data/de.xml | 21 ++- SafeExamBrowser.I18n/Data/en.xml | 21 ++- ...afeExamBrowser.Monitoring.Contracts.csproj | 2 + .../Events/SessionSwitchedEventHandler.cs | 15 +++ .../System/ISystemMonitor.cs | 33 +++++ .../SafeExamBrowser.Monitoring.csproj | 1 + .../System/SystemMonitor.cs | 96 ++++++++++++++ 15 files changed, 391 insertions(+), 49 deletions(-) create mode 100644 SafeExamBrowser.Client.UnitTests/Operations/SystemMonitorOperationTests.cs create mode 100644 SafeExamBrowser.Client/Operations/SystemMonitorOperation.cs create mode 100644 SafeExamBrowser.Monitoring.Contracts/System/Events/SessionSwitchedEventHandler.cs create mode 100644 SafeExamBrowser.Monitoring.Contracts/System/ISystemMonitor.cs create mode 100644 SafeExamBrowser.Monitoring/System/SystemMonitor.cs diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index 74e009ca..d54254ab 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -27,6 +27,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Display; +using SafeExamBrowser.Monitoring.Contracts.System; using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog; @@ -59,6 +60,7 @@ namespace SafeExamBrowser.Client.UnitTests private AppSettings settings; private Mock shutdown; private Mock splashScreen; + private Mock systemMonitor; private Mock taskbar; private Mock text; private Mock uiFactory; @@ -86,6 +88,7 @@ namespace SafeExamBrowser.Client.UnitTests settings = new AppSettings(); shutdown = new Mock(); splashScreen = new Mock(); + systemMonitor = new Mock(); taskbar = new Mock(); text = new Mock(); uiFactory = new Mock(); @@ -108,6 +111,7 @@ namespace SafeExamBrowser.Client.UnitTests runtimeProxy.Object, shutdown.Object, splashScreen.Object, + systemMonitor.Object, taskbar.Object, text.Object, uiFactory.Object); diff --git a/SafeExamBrowser.Client.UnitTests/Operations/SystemMonitorOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/SystemMonitorOperationTests.cs new file mode 100644 index 00000000..7c288c4d --- /dev/null +++ b/SafeExamBrowser.Client.UnitTests/Operations/SystemMonitorOperationTests.cs @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using SafeExamBrowser.Client.Operations; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts.System; + +namespace SafeExamBrowser.Client.UnitTests.Operations +{ + [TestClass] + public class SystemMonitorOperationTests + { + private ClientContext context; + private Mock systemMonitor; + private Mock logger; + private SystemMonitorOperation sut; + + [TestInitialize] + public void Initialize() + { + context = new ClientContext(); + systemMonitor = new Mock(); + logger = new Mock(); + + sut = new SystemMonitorOperation(context, systemMonitor.Object, logger.Object); + } + + [TestMethod] + public void Perform_MustStartMonitor() + { + sut.Perform(); + + systemMonitor.Verify(s => s.Start(), Times.Once); + systemMonitor.VerifyNoOtherCalls(); + } + + [TestMethod] + public void Revert_MustStopMonitor() + { + sut.Revert(); + + systemMonitor.Verify(s => s.Stop(), Times.Once); + systemMonitor.VerifyNoOtherCalls(); + } + } +} diff --git a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj index 4a44a661..46a6bb67 100644 --- a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj +++ b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj @@ -95,6 +95,7 @@ + diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index bbef75f0..ddcb55e0 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -25,6 +25,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Display; +using SafeExamBrowser.Monitoring.Contracts.System; using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog; @@ -49,8 +50,10 @@ namespace SafeExamBrowser.Client private IMessageBox messageBox; private IOperationSequence operations; private IRuntimeProxy runtime; + private bool sessionLocked; private Action shutdown; private ISplashScreen splashScreen; + private ISystemMonitor systemMonitor; private ITaskbar taskbar; private IText text; private IUserInterfaceFactory uiFactory; @@ -73,6 +76,7 @@ namespace SafeExamBrowser.Client IRuntimeProxy runtime, Action shutdown, ISplashScreen splashScreen, + ISystemMonitor systemMonitor, ITaskbar taskbar, IText text, IUserInterfaceFactory uiFactory) @@ -90,6 +94,7 @@ namespace SafeExamBrowser.Client this.runtime = runtime; this.shutdown = shutdown; this.splashScreen = splashScreen; + this.systemMonitor = systemMonitor; this.taskbar = taskbar; this.text = text; this.uiFactory = uiFactory; @@ -184,6 +189,7 @@ namespace SafeExamBrowser.Client ClientHost.Shutdown += ClientHost_Shutdown; displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged; runtime.ConnectionLost += Runtime_ConnectionLost; + systemMonitor.SessionSwitched += SystemMonitor_SessionSwitched; taskbar.QuitButtonClicked += Shell_QuitButtonClicked; foreach (var activator in context.Activators.OfType()) @@ -199,6 +205,7 @@ namespace SafeExamBrowser.Client applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed; displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged; runtime.ConnectionLost -= Runtime_ConnectionLost; + systemMonitor.SessionSwitched -= SystemMonitor_SessionSwitched; taskbar.QuitButtonClicked -= Shell_QuitButtonClicked; if (Browser != null) @@ -280,48 +287,14 @@ namespace SafeExamBrowser.Client private void ApplicationMonitor_TerminationFailed(IEnumerable applications) { var applicationList = string.Join(Environment.NewLine, applications.Select(a => $"- {a.Name}")); - var message = $"{text.Get(TextKey.LockScreen_Message)}{Environment.NewLine}{Environment.NewLine}{applicationList}"; + var message = $"{text.Get(TextKey.LockScreen_ApplicationsMessage)}{Environment.NewLine}{Environment.NewLine}{applicationList}"; var title = text.Get(TextKey.LockScreen_Title); - var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash); - var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_AllowOption) }; - var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_TerminateOption) }; - var lockScreen = uiFactory.CreateLockScreen(message, title, new [] { allowOption, terminateOption }); - var result = default(LockScreenResult); + var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsAllowOption) }; + var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsTerminateOption) }; - logger.Warn("Showing lock screen due to failed termination of blacklisted application(s)!"); - PauseActivators(); - lockScreen.Show(); + logger.Warn("Detected termination failure of blacklisted application(s)!"); - for (var unlocked = false; !unlocked;) - { - result = lockScreen.WaitForResult(); - - if (hasQuitPassword) - { - var passwordHash = hashAlgorithm.GenerateHashFor(result.Password); - var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase); - - if (isCorrect) - { - logger.Info("The user entered the correct unlock password."); - unlocked = true; - } - else - { - logger.Info("The user entered the wrong unlock password."); - messageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: lockScreen); - } - } - else - { - logger.Warn($"No unlock password is defined, allowing user to resume session!"); - unlocked = true; - } - } - - lockScreen.Close(); - ResumeActivators(); - logger.Info("Closed lock screen."); + var result = ShowLockScreen(message, title, new[] { allowOption, terminateOption }); if (result.OptionId == allowOption.Id) { @@ -524,6 +497,35 @@ namespace SafeExamBrowser.Client ResumeActivators(); } + private void SystemMonitor_SessionSwitched() + { + var message = text.Get(TextKey.LockScreen_UserSessionMessage); + var title = text.Get(TextKey.LockScreen_Title); + var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionContinueOption) }; + var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionTerminateOption) }; + + logger.Warn("Detected user session switch!"); + + if (!sessionLocked) + { + sessionLocked = true; + + var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption }); + + if (result.OptionId == terminateOption.Id) + { + logger.Info("Attempting to shutdown as requested by the user..."); + TryRequestShutdown(); + } + + sessionLocked = false; + } + else + { + logger.Info("Lock screen is already active."); + } + } + private void TerminationActivator_Activated() { PauseActivators(); @@ -599,6 +601,50 @@ namespace SafeExamBrowser.Client } } + private LockScreenResult ShowLockScreen(string message, string title, IEnumerable options) + { + var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash); + var lockScreen = uiFactory.CreateLockScreen(message, title, options); + var result = default(LockScreenResult); + + logger.Info("Showing lock screen..."); + PauseActivators(); + lockScreen.Show(); + + for (var unlocked = false; !unlocked;) + { + result = lockScreen.WaitForResult(); + + if (hasQuitPassword) + { + var passwordHash = hashAlgorithm.GenerateHashFor(result.Password); + var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase); + + if (isCorrect) + { + logger.Info("The user entered the correct unlock password."); + unlocked = true; + } + else + { + logger.Info("The user entered the wrong unlock password."); + messageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: lockScreen); + } + } + else + { + logger.Warn($"No unlock password is defined, allowing user to resume session!"); + unlocked = true; + } + } + + lockScreen.Close(); + ResumeActivators(); + logger.Info("Closed lock screen."); + + return result; + } + private bool TryInitiateShutdown() { var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash); diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 42096545..3a885aee 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -30,6 +30,7 @@ using SafeExamBrowser.Monitoring.Applications; using SafeExamBrowser.Monitoring.Display; using SafeExamBrowser.Monitoring.Keyboard; using SafeExamBrowser.Monitoring.Mouse; +using SafeExamBrowser.Monitoring.System; using SafeExamBrowser.Settings.Logging; using SafeExamBrowser.Settings.UserInterface; using SafeExamBrowser.SystemComponents; @@ -100,6 +101,7 @@ namespace SafeExamBrowser.Client var fileSystemDialog = BuildFileSystemDialog(); var hashAlgorithm = new HashAlgorithm(); var splashScreen = uiFactory.CreateSplashScreen(); + var systemMonitor = new SystemMonitor(ModuleLogger(nameof(SystemMonitor))); var operations = new Queue(); @@ -113,6 +115,7 @@ namespace SafeExamBrowser.Client operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation)); operations.Enqueue(new ApplicationOperation(context, applicationFactory, applicationMonitor, logger, text)); operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar)); + operations.Enqueue(new SystemMonitorOperation(context, systemMonitor, logger)); operations.Enqueue(new LazyInitializationOperation(BuildShellOperation)); operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new ClipboardOperation(context, logger, nativeMethods)); @@ -133,6 +136,7 @@ namespace SafeExamBrowser.Client runtimeProxy, shutdown, splashScreen, + systemMonitor, taskbar, text, uiFactory); diff --git a/SafeExamBrowser.Client/Operations/SystemMonitorOperation.cs b/SafeExamBrowser.Client/Operations/SystemMonitorOperation.cs new file mode 100644 index 00000000..aa21dccd --- /dev/null +++ b/SafeExamBrowser.Client/Operations/SystemMonitorOperation.cs @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Core.Contracts.OperationModel; +using SafeExamBrowser.Core.Contracts.OperationModel.Events; +using SafeExamBrowser.I18n.Contracts; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts.System; + +namespace SafeExamBrowser.Client.Operations +{ + internal class SystemMonitorOperation : ClientOperation + { + private readonly ILogger logger; + private readonly ISystemMonitor systemMonitor; + + public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } + public override event StatusChangedEventHandler StatusChanged; + + public SystemMonitorOperation(ClientContext context, ISystemMonitor systemMonitor, ILogger logger) : base(context) + { + this.logger = logger; + this.systemMonitor = systemMonitor; + } + + public override OperationResult Perform() + { + logger.Info("Initializing system events..."); + StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSystemEvents); + + systemMonitor.Start(); + + return OperationResult.Success; + } + + public override OperationResult Revert() + { + logger.Info("Finalizing system events..."); + StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeSystemEvents); + + systemMonitor.Stop(); + + return OperationResult.Success; + } + } +} diff --git a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj index 6a6010cc..3d63f785 100644 --- a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj +++ b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj @@ -95,6 +95,7 @@ + Code diff --git a/SafeExamBrowser.I18n.Contracts/TextKey.cs b/SafeExamBrowser.I18n.Contracts/TextKey.cs index 37e0744b..bfa415d8 100644 --- a/SafeExamBrowser.I18n.Contracts/TextKey.cs +++ b/SafeExamBrowser.I18n.Contracts/TextKey.cs @@ -44,9 +44,12 @@ namespace SafeExamBrowser.I18n.Contracts FileSystemDialog_Select, FileSystemDialog_Title, FolderDialog_ApplicationLocation, - LockScreen_AllowOption, - LockScreen_Message, - LockScreen_TerminateOption, + LockScreen_ApplicationsAllowOption, + LockScreen_ApplicationsMessage, + LockScreen_ApplicationsTerminateOption, + LockScreen_UserSessionContinueOption, + LockScreen_UserSessionMessage, + LockScreen_UserSessionTerminateOption, LockScreen_Title, LockScreen_UnlockButton, LogWindow_AlwaysOnTop, @@ -117,13 +120,16 @@ namespace SafeExamBrowser.I18n.Contracts OperationStatus_EmptyClipboard, OperationStatus_FinalizeApplications, OperationStatus_FinalizeServiceSession, + OperationStatus_FinalizeSystemEvents, OperationStatus_InitializeApplications, OperationStatus_InitializeBrowser, OperationStatus_InitializeConfiguration, OperationStatus_InitializeKioskMode, OperationStatus_InitializeRuntimeConnection, OperationStatus_InitializeServiceSession, + OperationStatus_InitializeSession, OperationStatus_InitializeShell, + OperationStatus_InitializeSystemEvents, OperationStatus_InitializeWorkingArea, OperationStatus_RestartCommunicationHost, OperationStatus_RestoreWorkingArea, @@ -132,7 +138,6 @@ namespace SafeExamBrowser.I18n.Contracts OperationStatus_StartCommunicationHost, OperationStatus_StartKeyboardInterception, OperationStatus_StartMouseInterception, - OperationStatus_InitializeSession, OperationStatus_StopClient, OperationStatus_StopCommunicationHost, OperationStatus_StopKeyboardInterception, diff --git a/SafeExamBrowser.I18n/Data/de.xml b/SafeExamBrowser.I18n/Data/de.xml index e74a1195..93120c27 100644 --- a/SafeExamBrowser.I18n/Data/de.xml +++ b/SafeExamBrowser.I18n/Data/de.xml @@ -90,13 +90,13 @@ Applikation "%%NAME%%" konnte nicht gefunden werden auf dem System! Bitte geben Sie an, wo die ausführbare Datei "%%EXECUTABLE%%" liegt. - + Verbotene Applikationen temporär erlauben. Dies gilt nur für die momentan laufenden Instanzen der aktuellen Sitzung! - + Die unten aufgelisteten, verbotenen Applikationen wurden gestartet und konnten nicht automatisch beendet werden! Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren. - + Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet! @@ -105,6 +105,15 @@ Entsperren + + Safe Exam Browser entsperren. + + + Der aktive Benutzer hat sich geändert oder der Computer wurde gesperrt! Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren. + + + Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet! + Immer zuoberst @@ -309,6 +318,9 @@ Beende Service-Sitzung + + Finalisiere System-Ereignisse + Initialisiere Applikationen @@ -333,6 +345,9 @@ Initialisiere Benutzeroberfläche + + Initialisiere System-Ereignisse + Initialisiere Arbeitsbereich diff --git a/SafeExamBrowser.I18n/Data/en.xml b/SafeExamBrowser.I18n/Data/en.xml index a3d85b72..f7eee548 100644 --- a/SafeExamBrowser.I18n/Data/en.xml +++ b/SafeExamBrowser.I18n/Data/en.xml @@ -90,13 +90,13 @@ Application "%%NAME%%" could not be found on the system! Please locate the folder containing the main executable "%%EXECUTABLE%%". - + Temporarily allow the blacklisted applications. This applies only to the currently running instances and session! - + The blacklisted applications listed below were started and could not be automatically terminated! In order to unlock SEB, please select one of the available options and enter the correct unlock password. - + Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately! @@ -105,6 +105,15 @@ Unlock + + Unlock Safe Exam Browser. + + + The active user has changed or the computer has been locked! In order to unlock SEB, please select one of the available options and enter the correct unlock password. + + + Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately! + Always on top @@ -309,6 +318,9 @@ Finalizing service session + + Finalizing system events + Initializing applications @@ -333,6 +345,9 @@ Initializing user interface + + Initializing system events + Initializing working area diff --git a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj index b2ad98d9..ff181e47 100644 --- a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj +++ b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj @@ -65,6 +65,8 @@ + + diff --git a/SafeExamBrowser.Monitoring.Contracts/System/Events/SessionSwitchedEventHandler.cs b/SafeExamBrowser.Monitoring.Contracts/System/Events/SessionSwitchedEventHandler.cs new file mode 100644 index 00000000..aa180a59 --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/System/Events/SessionSwitchedEventHandler.cs @@ -0,0 +1,15 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +namespace SafeExamBrowser.Monitoring.Contracts.System.Events +{ + /// + /// Indicates that the active user session of the operating system has changed. + /// + public delegate void SessionSwitchedEventHandler(); +} diff --git a/SafeExamBrowser.Monitoring.Contracts/System/ISystemMonitor.cs b/SafeExamBrowser.Monitoring.Contracts/System/ISystemMonitor.cs new file mode 100644 index 00000000..15088f0b --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/System/ISystemMonitor.cs @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using SafeExamBrowser.Monitoring.Contracts.System.Events; + +namespace SafeExamBrowser.Monitoring.Contracts.System +{ + /// + /// Monitors the operating system, e.g. for user account related events. + /// + public interface ISystemMonitor + { + /// + /// Event fired when the active user session has changed. + /// + event SessionSwitchedEventHandler SessionSwitched; + + /// + /// Starts the monitoring. + /// + void Start(); + + /// + /// Stops the monitoring. + /// + void Stop(); + } +} diff --git a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj index 7cb3c832..1f8ca5f7 100644 --- a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj +++ b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj @@ -63,6 +63,7 @@ + diff --git a/SafeExamBrowser.Monitoring/System/SystemMonitor.cs b/SafeExamBrowser.Monitoring/System/SystemMonitor.cs new file mode 100644 index 00000000..bc726c00 --- /dev/null +++ b/SafeExamBrowser.Monitoring/System/SystemMonitor.cs @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2020 ETH Zürich, Educational Development and Technology (LET) + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +using System; +using System.Threading.Tasks; +using Microsoft.Win32; +using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts.System; +using SafeExamBrowser.Monitoring.Contracts.System.Events; + +namespace SafeExamBrowser.Monitoring.System +{ + public class SystemMonitor : ISystemMonitor + { + private readonly ILogger logger; + + public event SessionSwitchedEventHandler SessionSwitched; + + public SystemMonitor(ILogger logger) + { + this.logger = logger; + } + + public void Start() + { + SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown; + SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged; + SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged; + SystemEvents.SessionEnded += SystemEvents_SessionEnded; + SystemEvents.SessionEnding += SystemEvents_SessionEnding; + SystemEvents.SessionSwitch += SystemEvents_SessionSwitch; + SystemEvents.TimeChanged += SystemEvents_TimeChanged; + SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged; + logger.Info("Started monitoring the operating system."); + } + + public void Stop() + { + SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown; + SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged; + SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged; + SystemEvents.SessionEnded -= SystemEvents_SessionEnded; + SystemEvents.SessionEnding -= SystemEvents_SessionEnding; + SystemEvents.SessionSwitch -= SystemEvents_SessionSwitch; + SystemEvents.TimeChanged -= SystemEvents_TimeChanged; + SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged; + logger.Info("Stopped monitoring the operating system."); + } + + private void SystemEvents_EventsThreadShutdown(object sender, EventArgs e) + { + logger.Warn("System event thread is about to be terminated!"); + } + + private void SystemEvents_InstalledFontsChanged(object sender, EventArgs e) + { + logger.Info("Installed fonts changed."); + } + + private void SystemEvents_PowerModeChanged(object sender, PowerModeChangedEventArgs e) + { + logger.Info($"Power mode changed: {e.Mode}"); + } + + private void SystemEvents_SessionEnded(object sender, SessionEndedEventArgs e) + { + logger.Warn($"User session ended! Reason: {e.Reason}"); + } + + private void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e) + { + logger.Warn($"User session is ending! Reason: {e.Reason}"); + } + + private void SystemEvents_SessionSwitch(object sender, SessionSwitchEventArgs e) + { + logger.Info($"User session switch detected! Reason: {e.Reason}"); + Task.Run(() => SessionSwitched?.Invoke()); + } + + private void SystemEvents_TimeChanged(object sender, EventArgs e) + { + logger.Info("Time changed."); + } + + private void SystemEvents_UserPreferenceChanged(object sender, UserPreferenceChangedEventArgs e) + { + logger.Info($"User preference changed. Category: {e.Category}"); + } + } +}