SEBWIN-453: Implemented monitoring for kiosk mode Create New Desktop.
This commit is contained in:
parent
68f4349a4d
commit
5d05acb6d7
8 changed files with 143 additions and 5 deletions
|
@ -24,6 +24,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
private SessionConfiguration currentSession;
|
||||
private AppSettings currentSettings;
|
||||
private Mock<IDesktopFactory> desktopFactory;
|
||||
private Mock<IDesktopMonitor> desktopMonitor;
|
||||
private Mock<IExplorerShell> explorerShell;
|
||||
private Mock<ILogger> logger;
|
||||
private SessionConfiguration nextSession;
|
||||
|
@ -39,6 +40,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
currentSession = new SessionConfiguration();
|
||||
currentSettings = new AppSettings();
|
||||
desktopFactory = new Mock<IDesktopFactory>();
|
||||
desktopMonitor = new Mock<IDesktopMonitor>();
|
||||
explorerShell = new Mock<IExplorerShell>();
|
||||
logger = new Mock<ILogger>();
|
||||
nextSession = new SessionConfiguration();
|
||||
|
@ -51,7 +53,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
sessionContext.Current = currentSession;
|
||||
sessionContext.Next = nextSession;
|
||||
|
||||
sut = new KioskModeOperation(desktopFactory.Object, explorerShell.Object, logger.Object, processFactory.Object, sessionContext);
|
||||
sut = new KioskModeOperation(desktopFactory.Object, desktopMonitor.Object, explorerShell.Object, logger.Object, processFactory.Object, sessionContext);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -64,6 +66,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
var createNew = 0;
|
||||
var activate = 0;
|
||||
var setStartup = 0;
|
||||
var startMonitor = 0;
|
||||
|
||||
nextSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
||||
|
@ -71,6 +74,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
desktopFactory.Setup(f => f.CreateNew(It.IsAny<string>())).Callback(() => createNew = ++order).Returns(newDesktop.Object);
|
||||
newDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
|
||||
processFactory.SetupSet(f => f.StartupDesktop = It.IsAny<IDesktop>()).Callback(() => setStartup = ++order);
|
||||
desktopMonitor.Setup(m => m.Start(It.IsAny<IDesktop>())).Callback(() => startMonitor = ++order);
|
||||
|
||||
var result = sut.Perform();
|
||||
|
||||
|
@ -86,6 +90,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
Assert.AreEqual(2, createNew);
|
||||
Assert.AreEqual(3, activate);
|
||||
Assert.AreEqual(4, setStartup);
|
||||
Assert.AreEqual(5, startMonitor);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -366,6 +371,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
|
||||
desktopFactory.Verify(f => f.GetCurrent(), Times.Once);
|
||||
desktopFactory.Verify(f => f.CreateNew(It.IsAny<string>()), Times.Once);
|
||||
desktopMonitor.Verify(m => m.Start(It.IsAny<IDesktop>()), Times.Once);
|
||||
desktopMonitor.Verify(m => m.Stop(), Times.Never);
|
||||
explorerShell.VerifyNoOtherCalls();
|
||||
newDesktop.Verify(d => d.Activate(), Times.Once);
|
||||
newDesktop.Verify(d => d.Close(), Times.Never);
|
||||
|
@ -403,6 +410,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
var order = 0;
|
||||
var activate = 0;
|
||||
var setStartup = 0;
|
||||
var stopMonitor = 0;
|
||||
var close = 0;
|
||||
|
||||
currentSettings.Security.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
@ -415,6 +423,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
Assert.AreEqual(OperationResult.Success, performResult);
|
||||
|
||||
desktopFactory.Reset();
|
||||
desktopMonitor.Setup(m => m.Stop()).Callback(() => stopMonitor = ++order);
|
||||
explorerShell.Reset();
|
||||
originalDesktop.Reset();
|
||||
originalDesktop.Setup(d => d.Activate()).Callback(() => activate = ++order);
|
||||
|
@ -432,9 +441,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
|
||||
Assert.AreEqual(OperationResult.Success, performResult);
|
||||
Assert.AreEqual(OperationResult.Success, revertResult);
|
||||
Assert.AreEqual(1, activate);
|
||||
Assert.AreEqual(2, setStartup);
|
||||
Assert.AreEqual(3, close);
|
||||
Assert.AreEqual(1, stopMonitor);
|
||||
Assert.AreEqual(2, activate);
|
||||
Assert.AreEqual(3, setStartup);
|
||||
Assert.AreEqual(4, close);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
|
|
@ -62,6 +62,7 @@ namespace SafeExamBrowser.Runtime
|
|||
var nativeMethods = new NativeMethods();
|
||||
var uiFactory = new UserInterfaceFactory(text);
|
||||
var desktopFactory = new DesktopFactory(ModuleLogger(nameof(DesktopFactory)));
|
||||
var desktopMonitor = new DesktopMonitor(ModuleLogger(nameof(DesktopMonitor)));
|
||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||
var fileSystem = new FileSystem();
|
||||
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||
|
@ -91,7 +92,7 @@ namespace SafeExamBrowser.Runtime
|
|||
sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo));
|
||||
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
|
||||
sessionOperations.Enqueue(new ProctoringWorkaroundOperation(logger, sessionContext));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, desktopMonitor, explorerShell, logger, processFactory, sessionContext));
|
||||
sessionOperations.Enqueue(new ClientOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
|
||||
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
|
||||
|
||||
|
|
|
@ -20,6 +20,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
private IDesktop newDesktop;
|
||||
private IDesktop originalDesktop;
|
||||
private IDesktopFactory desktopFactory;
|
||||
private IDesktopMonitor desktopMonitor;
|
||||
private IExplorerShell explorerShell;
|
||||
private KioskMode? activeMode;
|
||||
private ILogger logger;
|
||||
|
@ -30,12 +31,14 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
public KioskModeOperation(
|
||||
IDesktopFactory desktopFactory,
|
||||
IDesktopMonitor desktopMonitor,
|
||||
IExplorerShell explorerShell,
|
||||
ILogger logger,
|
||||
IProcessFactory processFactory,
|
||||
SessionContext sessionContext) : base(sessionContext)
|
||||
{
|
||||
this.desktopFactory = desktopFactory;
|
||||
this.desktopMonitor = desktopMonitor;
|
||||
this.explorerShell = explorerShell;
|
||||
this.logger = logger;
|
||||
this.processFactory = processFactory;
|
||||
|
@ -129,10 +132,14 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
newDesktop.Activate();
|
||||
processFactory.StartupDesktop = newDesktop;
|
||||
logger.Info("Successfully activated new desktop.");
|
||||
|
||||
desktopMonitor.Start(newDesktop);
|
||||
}
|
||||
|
||||
private void CloseNewDesktop()
|
||||
{
|
||||
desktopMonitor.Stop();
|
||||
|
||||
if (originalDesktop != null)
|
||||
{
|
||||
originalDesktop.Activate();
|
||||
|
|
26
SafeExamBrowser.WindowsApi.Contracts/IDesktopMonitor.cs
Normal file
26
SafeExamBrowser.WindowsApi.Contracts/IDesktopMonitor.cs
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.WindowsApi.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides funcionality to monitor a desktop, i.e. ensure a given desktop remains active even when a desktop switch is performed.
|
||||
/// </summary>
|
||||
public interface IDesktopMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts to monitor the given desktop.
|
||||
/// </summary>
|
||||
void Start(IDesktop desktop);
|
||||
|
||||
/// <summary>
|
||||
/// Stops the monitoring.
|
||||
/// </summary>
|
||||
void Stop();
|
||||
}
|
||||
}
|
|
@ -67,6 +67,7 @@
|
|||
<Compile Include="IBounds.cs" />
|
||||
<Compile Include="IDesktop.cs" />
|
||||
<Compile Include="IDesktopFactory.cs" />
|
||||
<Compile Include="IDesktopMonitor.cs" />
|
||||
<Compile Include="IExplorerShell.cs" />
|
||||
<Compile Include="INativeMethods.cs" />
|
||||
<Compile Include="IProcess.cs" />
|
||||
|
|
89
SafeExamBrowser.WindowsApi/DesktopMonitor.cs
Normal file
89
SafeExamBrowser.WindowsApi/DesktopMonitor.cs
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
* Copyright (c) 2021 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.Runtime.InteropServices;
|
||||
using System.Timers;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.WindowsApi.Constants;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.WindowsApi
|
||||
{
|
||||
public class DesktopMonitor : IDesktopMonitor
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly Timer timer;
|
||||
|
||||
private IDesktop desktop;
|
||||
|
||||
public DesktopMonitor(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.timer = new Timer(1000);
|
||||
}
|
||||
|
||||
public void Start(IDesktop desktop)
|
||||
{
|
||||
this.desktop = desktop;
|
||||
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
|
||||
logger.Info($"Started monitoring desktop {desktop}.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Elapsed -= Timer_Elapsed;
|
||||
|
||||
logger.Info($"Stopped monitoring desktop {desktop}.");
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
var handle = User32.OpenInputDesktop(0, false, (uint) AccessMask.DESKTOP_NONE);
|
||||
var name = string.Empty;
|
||||
var nameLength = 0;
|
||||
|
||||
if (handle != IntPtr.Zero)
|
||||
{
|
||||
User32.GetUserObjectInformation(handle, Constant.UOI_NAME, IntPtr.Zero, 0, ref nameLength);
|
||||
|
||||
var namePointer = Marshal.AllocHGlobal(nameLength);
|
||||
var success = User32.GetUserObjectInformation(handle, Constant.UOI_NAME, namePointer, nameLength, ref nameLength);
|
||||
|
||||
if (success)
|
||||
{
|
||||
name = Marshal.PtrToStringAnsi(namePointer);
|
||||
Marshal.FreeHGlobal(namePointer);
|
||||
|
||||
if (name?.Equals(desktop.Name, StringComparison.OrdinalIgnoreCase) != true)
|
||||
{
|
||||
logger.Warn($"Detected desktop switch to '{name}' [{handle}], trying to reactivate {desktop}...");
|
||||
desktop.Activate();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to get name of currently active desktop!");
|
||||
}
|
||||
|
||||
User32.CloseDesktop(handle);
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to get currently active desktop!");
|
||||
}
|
||||
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,6 +65,7 @@
|
|||
<Compile Include="Delegates\HookDelegate.cs" />
|
||||
<Compile Include="Desktop.cs" />
|
||||
<Compile Include="DesktopFactory.cs" />
|
||||
<Compile Include="DesktopMonitor.cs" />
|
||||
<Compile Include="ExplorerShell.cs" />
|
||||
<Compile Include="Hooks\MouseHook.cs" />
|
||||
<Compile Include="Hooks\SystemHook.cs" />
|
||||
|
|
|
@ -77,6 +77,9 @@ namespace SafeExamBrowser.WindowsApi
|
|||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool OpenClipboard(IntPtr hWndNewOwner);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern IntPtr OpenInputDesktop(uint dwFlags, bool fInherit, uint dwDesiredAccess);
|
||||
|
||||
[DllImport("user32.dll", SetLastError = true)]
|
||||
internal static extern bool PostMessage(IntPtr hWnd, uint msg, IntPtr wParam, IntPtr lParam);
|
||||
|
||||
|
|
Loading…
Reference in a new issue