SEBWIN-917: Consolidated system (events) monitoring in sentinel.

This commit is contained in:
Damian Büchel 2024-07-25 12:22:49 +02:00
parent 1f50ab74c9
commit 6a77a41564
13 changed files with 102 additions and 219 deletions

View file

@ -73,7 +73,6 @@ namespace SafeExamBrowser.Client.UnitTests
private AppSettings settings;
private Mock<Action> shutdown;
private Mock<ISplashScreen> splashScreen;
private Mock<ISystemMonitor> systemMonitor;
private Mock<ITaskbar> taskbar;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
@ -108,7 +107,6 @@ namespace SafeExamBrowser.Client.UnitTests
settings = new AppSettings();
shutdown = new Mock<Action>();
splashScreen = new Mock<ISplashScreen>();
systemMonitor = new Mock<ISystemMonitor>();
taskbar = new Mock<ITaskbar>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
@ -134,7 +132,6 @@ namespace SafeExamBrowser.Client.UnitTests
runtimeProxy.Object,
shutdown.Object,
splashScreen.Object,
systemMonitor.Object,
sentinel.Object,
taskbar.Object,
text.Object,
@ -1288,7 +1285,7 @@ namespace SafeExamBrowser.Client.UnitTests
.Returns(lockScreen.Object);
sut.TryStart();
systemMonitor.Raise(m => m.SessionChanged += null);
sentinel.Raise(s => s.SessionChanged += null);
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
@ -1311,7 +1308,7 @@ namespace SafeExamBrowser.Client.UnitTests
.Returns(lockScreen.Object);
sut.TryStart();
systemMonitor.Raise(m => m.SessionChanged += null);
sentinel.Raise(s => s.SessionChanged += null);
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
@ -1333,7 +1330,7 @@ namespace SafeExamBrowser.Client.UnitTests
.Returns(lockScreen.Object);
sut.TryStart();
systemMonitor.Raise(m => m.SessionChanged += null);
sentinel.Raise(s => s.SessionChanged += null);
lockScreen.Verify(l => l.Show(), Times.Never);
}

View file

@ -1,53 +0,0 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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<ISystemMonitor> systemMonitor;
private Mock<ILogger> logger;
private SystemMonitorOperation sut;
[TestInitialize]
public void Initialize()
{
context = new ClientContext();
systemMonitor = new Mock<ISystemMonitor>();
logger = new Mock<ILogger>();
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();
}
}
}

View file

@ -163,7 +163,6 @@
<Compile Include="Communication\ClientHostTests.cs" />
<Compile Include="Notifications\AboutNotificationControllerTests.cs" />
<Compile Include="Notifications\LogNotificationControllerTests.cs" />
<Compile Include="Operations\SystemMonitorOperationTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ClientControllerTests.cs" />
<Compile Include="CoordinatorTests.cs" />

View file

@ -66,7 +66,6 @@ namespace SafeExamBrowser.Client
private readonly IRuntimeProxy runtime;
private readonly Action shutdown;
private readonly ISplashScreen splashScreen;
private readonly ISystemMonitor systemMonitor;
private readonly ISystemSentinel sentinel;
private readonly ITaskbar taskbar;
private readonly IText text;
@ -97,7 +96,6 @@ namespace SafeExamBrowser.Client
IRuntimeProxy runtime,
Action shutdown,
ISplashScreen splashScreen,
ISystemMonitor systemMonitor,
ISystemSentinel sentinel,
ITaskbar taskbar,
IText text,
@ -118,7 +116,6 @@ namespace SafeExamBrowser.Client
this.runtime = runtime;
this.shutdown = shutdown;
this.splashScreen = splashScreen;
this.systemMonitor = systemMonitor;
this.sentinel = sentinel;
this.taskbar = taskbar;
this.text = text;
@ -144,6 +141,7 @@ namespace SafeExamBrowser.Client
ShowShell();
AutoStartApplications();
ScheduleIntegrityVerification();
StartMonitoring();
var communication = runtime.InformClientReady();
@ -226,8 +224,8 @@ namespace SafeExamBrowser.Client
runtime.ConnectionLost += Runtime_ConnectionLost;
sentinel.CursorChanged += Sentinel_CursorChanged;
sentinel.EaseOfAccessChanged += Sentinel_EaseOfAccessChanged;
sentinel.SessionChanged += Sentinel_SessionChanged;
sentinel.StickyKeysChanged += Sentinel_StickyKeysChanged;
systemMonitor.SessionChanged += SystemMonitor_SessionChanged;
taskbar.LoseFocusRequested += Taskbar_LoseFocusRequested;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
@ -253,8 +251,8 @@ namespace SafeExamBrowser.Client
runtime.ConnectionLost -= Runtime_ConnectionLost;
sentinel.CursorChanged -= Sentinel_CursorChanged;
sentinel.EaseOfAccessChanged -= Sentinel_EaseOfAccessChanged;
sentinel.SessionChanged -= Sentinel_SessionChanged;
sentinel.StickyKeysChanged -= Sentinel_StickyKeysChanged;
systemMonitor.SessionChanged -= SystemMonitor_SessionChanged;
taskbar.LoseFocusRequested -= Taskbar_LoseFocusRequested;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
@ -368,6 +366,11 @@ namespace SafeExamBrowser.Client
timer.Elapsed += (o, args) => VerifyApplicationIntegrity();
timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
timer.Start();
}
private void StartMonitoring()
{
sentinel.StartMonitoringSystemEvents();
if (!Settings.Security.AllowStickyKeys)
{
@ -853,6 +856,47 @@ namespace SafeExamBrowser.Client
}
}
private void Sentinel_SessionChanged()
{
var allow = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
var disable = Settings.Security.DisableSessionChangeLockScreen;
if (allow || disable)
{
logger.Info($"Detected user session change, but {(allow ? "session locking and/or switching is allowed" : "lock screen is deactivated")}.");
}
else
{
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("User session changed! Attempting to show lock screen...");
if (coordinator.RequestSessionLock())
{
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == continueOption.Id)
{
logger.Info("The session will be allowed to resume as requested by the user...");
}
else if (result.OptionId == terminateOption.Id)
{
logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
logger.Warn("User session changed but lock screen is already active.");
}
}
}
private void Sentinel_StickyKeysChanged(SentinelEventArgs args)
{
if (coordinator.RequestSessionLock())
@ -919,42 +963,6 @@ namespace SafeExamBrowser.Client
ResumeActivators();
}
private void SystemMonitor_SessionChanged()
{
var allow = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
var disable = Settings.Security.DisableSessionChangeLockScreen;
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) };
if (allow || disable)
{
logger.Info($"Detected user session change, but {(allow ? "session locking and/or switching is allowed" : "lock screen is deactivated")}.");
}
else
{
logger.Warn("Detected user session change!");
if (coordinator.RequestSessionLock())
{
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
if (result.OptionId == terminateOption.Id)
{
logger.Info("Attempting to shutdown as requested by the user...");
TryRequestShutdown();
}
coordinator.ReleaseSessionLock();
}
else
{
logger.Info("Lock screen is already active.");
}
}
}
private void Taskbar_LoseFocusRequested(bool forward)
{
Browser.Focus(forward);

View file

@ -121,7 +121,6 @@ namespace SafeExamBrowser.Client
var hashAlgorithm = new HashAlgorithm();
var sentinel = new SystemSentinel(ModuleLogger(nameof(SystemSentinel)), nativeMethods, registry);
var splashScreen = uiFactory.CreateSplashScreen();
var systemMonitor = new SystemMonitor(ModuleLogger(nameof(SystemMonitor)));
var operations = new Queue<IOperation>();
@ -137,7 +136,6 @@ 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 LazyInitializationOperation(BuildServerOperation));
@ -162,7 +160,6 @@ namespace SafeExamBrowser.Client
runtimeProxy,
shutdown,
splashScreen,
systemMonitor,
sentinel,
taskbar,
text,

View file

@ -1,51 +0,0 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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;
}
}
}

View file

@ -97,7 +97,6 @@
<Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ServerOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\SystemMonitorOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>

View file

@ -73,7 +73,6 @@
<Compile Include="System\Events\SentinelEventArgs.cs" />
<Compile Include="System\Events\SentinelEventHandler.cs" />
<Compile Include="System\Events\SessionChangedEventHandler.cs" />
<Compile Include="System\ISystemMonitor.cs" />
<Compile Include="System\ISystemSentinel.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -1,33 +0,0 @@
/*
* Copyright (c) 2024 ETH Zürich, IT Services
*
* 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
{
/// <summary>
/// Monitors the operating system, e.g. for user account related events.
/// </summary>
public interface ISystemMonitor
{
/// <summary>
/// Event fired when the active user session has changed.
/// </summary>
event SessionChangedEventHandler SessionChanged;
/// <summary>
/// Starts the monitoring.
/// </summary>
void Start();
/// <summary>
/// Stops the monitoring.
/// </summary>
void Stop();
}
}

View file

@ -25,6 +25,11 @@ namespace SafeExamBrowser.Monitoring.Contracts.System
/// </summary>
event SentinelEventHandler EaseOfAccessChanged;
/// <summary>
/// Event fired when the active user session has changed.
/// </summary>
event SessionChangedEventHandler SessionChanged;
/// <summary>
/// Event fired when the sticky keys state has changed.
/// </summary>
@ -60,6 +65,11 @@ namespace SafeExamBrowser.Monitoring.Contracts.System
/// </summary>
void StartMonitoringStickyKeys();
/// <summary>
/// Starts monitoring the system events.
/// </summary>
void StartMonitoringSystemEvents();
/// <summary>
/// Stops all monitoring operations.
/// </summary>

View file

@ -70,7 +70,7 @@
<Compile Include="System\Components\Cursors.cs" />
<Compile Include="System\Components\EaseOfAccess.cs" />
<Compile Include="System\Components\StickyKeys.cs" />
<Compile Include="System\SystemMonitor.cs" />
<Compile Include="System\Components\SystemEvents.cs" />
<Compile Include="System\SystemSentinel.cs" />
</ItemGroup>
<ItemGroup>

View file

@ -10,46 +10,47 @@ 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
namespace SafeExamBrowser.Monitoring.System.Components
{
public class SystemMonitor : ISystemMonitor
internal class SystemEvents
{
private readonly ILogger logger;
public event SessionChangedEventHandler SessionChanged;
internal event SessionChangedEventHandler SessionChanged;
public SystemMonitor(ILogger logger)
internal SystemEvents(ILogger logger)
{
this.logger = logger;
}
public void Start()
internal void StartMonitoring()
{
SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown;
SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged;
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
SystemEvents.SessionEnded += SystemEvents_SessionEnded;
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
SystemEvents.SessionSwitch += SystemEvents_SessionChanged;
SystemEvents.TimeChanged += SystemEvents_TimeChanged;
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
logger.Info("Started monitoring the operating system.");
Microsoft.Win32.SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown;
Microsoft.Win32.SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged;
Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
Microsoft.Win32.SystemEvents.SessionEnded += SystemEvents_SessionEnded;
Microsoft.Win32.SystemEvents.SessionEnding += SystemEvents_SessionEnding;
Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionChanged;
Microsoft.Win32.SystemEvents.TimeChanged += SystemEvents_TimeChanged;
Microsoft.Win32.SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
logger.Info("Started monitoring system events.");
}
public void Stop()
internal void StopMonitoring()
{
SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown;
SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged;
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
SystemEvents.SessionEnded -= SystemEvents_SessionEnded;
SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
SystemEvents.SessionSwitch -= SystemEvents_SessionChanged;
SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
logger.Info("Stopped monitoring the operating system.");
Microsoft.Win32.SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown;
Microsoft.Win32.SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged;
Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
Microsoft.Win32.SystemEvents.SessionEnded -= SystemEvents_SessionEnded;
Microsoft.Win32.SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
Microsoft.Win32.SystemEvents.SessionSwitch -= SystemEvents_SessionChanged;
Microsoft.Win32.SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
Microsoft.Win32.SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
logger.Info("Stopped monitoring system events.");
}
private void SystemEvents_EventsThreadShutdown(object sender, EventArgs e)

View file

@ -20,16 +20,19 @@ namespace SafeExamBrowser.Monitoring.System
private readonly Cursors cursors;
private readonly EaseOfAccess easeOfAccess;
private readonly StickyKeys stickyKeys;
private readonly SystemEvents systemEvents;
public event SentinelEventHandler CursorChanged;
public event SentinelEventHandler EaseOfAccessChanged;
public event SentinelEventHandler StickyKeysChanged;
public event SessionChangedEventHandler SessionChanged;
public SystemSentinel(ILogger logger, INativeMethods nativeMethods, IRegistry registry)
{
this.cursors = new Cursors(logger, registry);
this.easeOfAccess = new EaseOfAccess(logger, registry);
this.stickyKeys = new StickyKeys(logger, nativeMethods);
cursors = new Cursors(logger, registry);
easeOfAccess = new EaseOfAccess(logger, registry);
stickyKeys = new StickyKeys(logger, nativeMethods);
systemEvents = new SystemEvents(logger);
}
public bool DisableStickyKeys()
@ -65,11 +68,18 @@ namespace SafeExamBrowser.Monitoring.System
stickyKeys.StartMonitoring();
}
public void StartMonitoringSystemEvents()
{
systemEvents.SessionChanged += () => SessionChanged?.Invoke();
systemEvents.StartMonitoring();
}
public void StopMonitoring()
{
cursors.StopMonitoring();
easeOfAccess.StopMonitoring();
stickyKeys.StopMonitoring();
systemEvents.StopMonitoring();
}
public bool VerifyCursors()