SEBWIN-311: Started implementing scaffolding for third-party applications & monitoring. Renamed ApplicationSettings to AppSettings, resolved dependency from WindowsApi on Monitoring namespace and introduced ClientContext for runtime data of the client.

This commit is contained in:
dbuechel 2019-10-01 11:30:53 +02:00
parent b2013412dd
commit 8fd22032b6
85 changed files with 1145 additions and 1003 deletions

View file

@ -10,6 +10,7 @@ using System;
using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Client.Contracts
{
@ -41,7 +42,7 @@ namespace SafeExamBrowser.Client.Contracts
/// <summary>
/// The settings to be used during application execution.
/// </summary>
Settings.ApplicationSettings Settings { set; }
AppSettings Settings { set; }
/// <summary>
/// Reverts any changes, releases all used resources and terminates the client.

View file

@ -17,14 +17,13 @@ using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.Processes;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -38,6 +37,7 @@ namespace SafeExamBrowser.Client.UnitTests
{
private AppConfig appConfig;
private Mock<IActionCenter> actionCenter;
private Mock<IApplicationMonitor> applicationMonitor;
private Mock<IBrowserApplication> browserController;
private Mock<IClientHost> clientHost;
private Mock<IDisplayMonitor> displayMonitor;
@ -45,17 +45,15 @@ namespace SafeExamBrowser.Client.UnitTests
private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox;
private Mock<IProcessMonitor> processMonitor;
private Mock<IOperationSequence> operationSequence;
private Mock<IRuntimeProxy> runtimeProxy;
private Guid sessionId;
private ApplicationSettings settings;
private AppSettings settings;
private Mock<Action> shutdown;
private Mock<ITaskbar> taskbar;
private Mock<ITerminationActivator> terminationActivator;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
private Mock<IWindowMonitor> windowMonitor;
private ClientController sut;
@ -64,6 +62,7 @@ namespace SafeExamBrowser.Client.UnitTests
{
appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>();
applicationMonitor = new Mock<IApplicationMonitor>();
browserController = new Mock<IBrowserApplication>();
clientHost = new Mock<IClientHost>();
displayMonitor = new Mock<IDisplayMonitor>();
@ -71,17 +70,15 @@ namespace SafeExamBrowser.Client.UnitTests
hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>();
processMonitor = new Mock<IProcessMonitor>();
operationSequence = new Mock<IOperationSequence>();
runtimeProxy = new Mock<IRuntimeProxy>();
sessionId = Guid.NewGuid();
settings = new ApplicationSettings();
settings = new AppSettings();
shutdown = new Mock<Action>();
taskbar = new Mock<ITaskbar>();
terminationActivator = new Mock<ITerminationActivator>();
text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>();
windowMonitor = new Mock<IWindowMonitor>();
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true));
@ -89,20 +86,19 @@ namespace SafeExamBrowser.Client.UnitTests
sut = new ClientController(
actionCenter.Object,
applicationMonitor.Object,
displayMonitor.Object,
explorerShell.Object,
hashAlgorithm.Object,
logger.Object,
messageBox.Object,
operationSequence.Object,
processMonitor.Object,
runtimeProxy.Object,
shutdown.Object,
taskbar.Object,
terminationActivator.Object,
text.Object,
uiFactory.Object,
windowMonitor.Object);
uiFactory.Object);
sut.AppConfig = appConfig;
sut.Browser = browserController.Object;
@ -111,6 +107,30 @@ namespace SafeExamBrowser.Client.UnitTests
sut.Settings = settings;
}
[TestMethod]
public void ApplicationMonitor_MustHandleExplorerStartCorrectly()
{
var order = 0;
var shell = 0;
var workingArea = 0;
var bounds = 0;
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => bounds = ++order);
sut.TryStart();
applicationMonitor.Raise(p => p.ExplorerStarted += null);
explorerShell.Verify(p => p.Terminate(), Times.Once);
displayMonitor.Verify(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight()), Times.Once);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
Assert.IsTrue(shell == 1);
Assert.IsTrue(workingArea == 2);
Assert.IsTrue(bounds == 3);
}
[TestMethod]
public void Communication_MustCorrectlyHandleMessageBoxRequest()
{
@ -265,30 +285,6 @@ namespace SafeExamBrowser.Client.UnitTests
splashScreen.Verify(s => s.UpdateStatus(It.Is<TextKey>(k => k == key), It.IsAny<bool>()), Times.Once);
}
[TestMethod]
public void ProcessMonitor_MustHandleExplorerStartCorrectly()
{
var order = 0;
var shell = 0;
var workingArea = 0;
var bounds = 0;
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => bounds = ++order);
sut.TryStart();
processMonitor.Raise(p => p.ExplorerStarted += null);
explorerShell.Verify(p => p.Terminate(), Times.Once);
displayMonitor.Verify(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight()), Times.Once);
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
Assert.IsTrue(shell == 1);
Assert.IsTrue(workingArea == 2);
Assert.IsTrue(bounds == 3);
}
[TestMethod]
public void Reconfiguration_MustDenyIfInExamMode()
{
@ -642,67 +638,5 @@ namespace SafeExamBrowser.Client.UnitTests
terminationActivator.Verify(t => t.Resume(), Times.Once);
runtimeProxy.Verify(p => p.RequestShutdown(), Times.Once);
}
[TestMethod]
public void WindowMonitor_MustHandleAllowedWindowChangeCorrectly()
{
var window = new IntPtr(12345);
processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(true);
sut.TryStart();
windowMonitor.Raise(w => w.WindowChanged += null, window);
processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once);
windowMonitor.Verify(w => w.Hide(window), Times.Never);
windowMonitor.Verify(w => w.Close(window), Times.Never);
}
[TestMethod]
public void WindowMonitor_MustHandleUnallowedWindowHideCorrectly()
{
var order = 0;
var belongs = 0;
var hide = 0;
var window = new IntPtr(12345);
processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(false).Callback(() => belongs = ++order);
windowMonitor.Setup(w => w.Hide(window)).Returns(true).Callback(() => hide = ++order);
sut.TryStart();
windowMonitor.Raise(w => w.WindowChanged += null, window);
processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once);
windowMonitor.Verify(w => w.Hide(window), Times.Once);
windowMonitor.Verify(w => w.Close(window), Times.Never);
Assert.IsTrue(belongs == 1);
Assert.IsTrue(hide == 2);
}
[TestMethod]
public void WindowMonitor_MustHandleUnallowedWindowCloseCorrectly()
{
var order = 0;
var belongs = 0;
var hide = 0;
var close = 0;
var window = new IntPtr(12345);
processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(false).Callback(() => belongs = ++order);
windowMonitor.Setup(w => w.Hide(window)).Returns(false).Callback(() => hide = ++order);
windowMonitor.Setup(w => w.Close(window)).Callback(() => close = ++order);
sut.TryStart();
windowMonitor.Raise(w => w.WindowChanged += null, window);
processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once);
windowMonitor.Verify(w => w.Hide(window), Times.Once);
windowMonitor.Verify(w => w.Close(window), Times.Once);
Assert.IsTrue(belongs == 1);
Assert.IsTrue(hide == 2);
Assert.IsTrue(close == 3);
}
}
}

View file

@ -0,0 +1,22 @@
/*
* Copyright (c) 2019 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;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
[TestClass]
public class ApplicationOperationTests
{
[TestMethod]
public void TODO()
{
Assert.Fail();
}
}
}

View file

@ -13,9 +13,9 @@ using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Communication.Contracts.Data;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
@ -23,6 +23,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public class ConfigurationOperationTests
{
private ClientConfiguration configuration;
private ClientContext context;
private Mock<ILogger> logger;
private Mock<IRuntimeProxy> runtime;
private ConfigurationOperation sut;
@ -31,10 +32,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void Initialize()
{
configuration = new ClientConfiguration();
context = new ClientContext();
logger = new Mock<ILogger>();
runtime = new Mock<IRuntimeProxy>();
sut = new ConfigurationOperation(configuration, logger.Object, runtime.Object);
sut = new ConfigurationOperation(configuration, context, logger.Object, runtime.Object);
}
[TestMethod]
@ -46,7 +48,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
AppConfig = new AppConfig(),
SessionId = Guid.NewGuid(),
Settings = new ApplicationSettings()
Settings = new AppSettings()
}
};

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
private Mock<IKeyboardInterceptor> keyboardInterceptorMock;
private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private KeyboardInterceptorOperation sut;
@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
keyboardInterceptorMock = new Mock<IKeyboardInterceptor>();
loggerMock = new Mock<ILogger>();
nativeMethodsMock = new Mock<INativeMethods>();
sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object, nativeMethodsMock.Object);
sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object);
}
[TestMethod]
@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
sut.Perform();
nativeMethodsMock.Verify(n => n.RegisterKeyboardHook(It.IsAny<IKeyboardInterceptor>()), Times.Once);
keyboardInterceptorMock.Verify(i => i.Start(), Times.Once);
keyboardInterceptorMock.Verify(i => i.Stop(), Times.Never);
}
[TestMethod]
@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
sut.Revert();
nativeMethodsMock.Verify(n => n.DeregisterKeyboardHook(It.IsAny<IKeyboardInterceptor>()), Times.Once);
keyboardInterceptorMock.Verify(i => i.Start(), Times.Never);
keyboardInterceptorMock.Verify(i => i.Stop(), Times.Once);
}
}
}

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
private Mock<IMouseInterceptor> mouseInterceptorMock;
private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private MouseInterceptorOperation sut;
@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
mouseInterceptorMock = new Mock<IMouseInterceptor>();
loggerMock = new Mock<ILogger>();
nativeMethodsMock = new Mock<INativeMethods>();
sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object, nativeMethodsMock.Object);
sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object);
}
[TestMethod]
@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
sut.Perform();
nativeMethodsMock.Verify(n => n.RegisterMouseHook(It.IsAny<IMouseInterceptor>()), Times.Once);
mouseInterceptorMock.Verify(i => i.Start(), Times.Once);
mouseInterceptorMock.Verify(i => i.Stop(), Times.Never);
}
[TestMethod]
@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{
sut.Revert();
nativeMethodsMock.Verify(n => n.DeregisterMouseHook(It.IsAny<IMouseInterceptor>()), Times.Once);
mouseInterceptorMock.Verify(i => i.Start(), Times.Never);
mouseInterceptorMock.Verify(i => i.Stop(), Times.Once);
}
}
}

View file

@ -1,74 +0,0 @@
/*
* Copyright (c) 2019 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.Settings;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Processes;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
[TestClass]
public class ProcessMonitorOperationTests
{
private Mock<ILogger> logger;
private Mock<IProcessMonitor> processMonitor;
private ApplicationSettings settings;
private ProcessMonitorOperation sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
processMonitor = new Mock<IProcessMonitor>();
settings = new ApplicationSettings();
sut = new ProcessMonitorOperation(logger.Object, processMonitor.Object,settings);
}
[TestMethod]
public void MustObserveExplorerWithDisableExplorerShell()
{
var counter = 0;
var start = 0;
var stop = 0;
settings.KioskMode = KioskMode.DisableExplorerShell;
processMonitor.Setup(p => p.StartMonitoringExplorer()).Callback(() => start = ++counter);
processMonitor.Setup(p => p.StopMonitoringExplorer()).Callback(() => stop = ++counter);
sut.Perform();
sut.Revert();
processMonitor.Verify(p => p.StartMonitoringExplorer(), Times.Once);
processMonitor.Verify(p => p.StopMonitoringExplorer(), Times.Once);
Assert.AreEqual(1, start);
Assert.AreEqual(2, stop);
}
[TestMethod]
public void MustNotObserveExplorerWithOtherKioskModes()
{
settings.KioskMode = KioskMode.CreateNewDesktop;
sut.Perform();
sut.Revert();
settings.KioskMode = KioskMode.None;
sut.Perform();
sut.Revert();
processMonitor.Verify(p => p.StartMonitoringExplorer(), Times.Never);
processMonitor.Verify(p => p.StopMonitoringExplorer(), Times.Never);
}
}
}

View file

@ -1,82 +0,0 @@
/*
* Copyright (c) 2019 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.Settings;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Windows;
namespace SafeExamBrowser.Client.UnitTests.Operations
{
[TestClass]
public class WindowMonitorOperationTests
{
private Mock<ILogger> loggerMock;
private Mock<IWindowMonitor> windowMonitorMock;
[TestInitialize]
public void Initialize()
{
loggerMock = new Mock<ILogger>();
windowMonitorMock = new Mock<IWindowMonitor>();
}
[TestMethod]
public void MustPerformCorrectlyForCreateNewDesktop()
{
var sut = new WindowMonitorOperation(KioskMode.CreateNewDesktop, loggerMock.Object, windowMonitorMock.Object);
sut.Perform();
windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
}
[TestMethod]
public void MustRevertCorrectlyForCreateNewDesktop()
{
var sut = new WindowMonitorOperation(KioskMode.CreateNewDesktop, loggerMock.Object, windowMonitorMock.Object);
sut.Revert();
windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
}
[TestMethod]
public void MustPerformCorrectlyForDisableExplorerShell()
{
var sut = new WindowMonitorOperation(KioskMode.DisableExplorerShell, loggerMock.Object, windowMonitorMock.Object);
sut.Perform();
windowMonitorMock.Verify(w => w.StartMonitoringWindows(), Times.Once);
}
[TestMethod]
public void MustRevertCorrectlyForDisableExplorerShell()
{
var sut = new WindowMonitorOperation(KioskMode.DisableExplorerShell, loggerMock.Object, windowMonitorMock.Object);
sut.Revert();
windowMonitorMock.Verify(w => w.StopMonitoringWindows(), Times.Once);
}
[TestMethod]
public void MustDoNothingWithoutKioskMode()
{
var sut = new WindowMonitorOperation(KioskMode.None, loggerMock.Object, windowMonitorMock.Object);
sut.Perform();
sut.Revert();
windowMonitorMock.VerifyNoOtherCalls();
}
}
}

View file

@ -81,6 +81,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="Operations\ApplicationOperationTests.cs" />
<Compile Include="Operations\BrowserOperationTests.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperationTests.cs" />
<Compile Include="Operations\ClipboardOperationTests.cs" />
@ -88,10 +89,8 @@
<Compile Include="Operations\DisplayMonitorOperationTests.cs" />
<Compile Include="Operations\KeyboardInterceptorOperationTests.cs" />
<Compile Include="Operations\MouseInterceptorOperationTests.cs" />
<Compile Include="Operations\ProcessMonitorOperationTests.cs" />
<Compile Include="Operations\RuntimeConnectionOperationTests.cs" />
<Compile Include="Operations\ShellOperationTests.cs" />
<Compile Include="Operations\WindowMonitorOperationTests.cs" />
<Compile Include="Communication\ClientHostTests.cs" />
<Compile Include="Notifications\AboutNotificationControllerTests.cs" />
<Compile Include="Notifications\LogNotificationControllerTests.cs" />

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019 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.Configuration.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Client
{
/// <summary>
/// Holds all configuration and runtime data for the client.
/// </summary>
internal class ClientContext
{
/// <summary>
/// The global application configuration.
/// </summary>
internal AppConfig AppConfig { get; set; }
/// <summary>
/// The settings for the current session.
/// </summary>
internal AppSettings Settings { get; set; }
}
}

View file

@ -17,14 +17,13 @@ using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.Processes;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -36,13 +35,13 @@ namespace SafeExamBrowser.Client
internal class ClientController : IClientController
{
private IActionCenter actionCenter;
private IApplicationMonitor applicationMonitor;
private IDisplayMonitor displayMonitor;
private IExplorerShell explorerShell;
private IHashAlgorithm hashAlgorithm;
private ILogger logger;
private IMessageBox messageBox;
private IOperationSequence operations;
private IProcessMonitor processMonitor;
private IRuntimeProxy runtime;
private Action shutdown;
private ISplashScreen splashScreen;
@ -50,13 +49,12 @@ namespace SafeExamBrowser.Client
private ITerminationActivator terminationActivator;
private IText text;
private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
private AppConfig appConfig;
public IBrowserApplication Browser { private get; set; }
public IClientHost ClientHost { private get; set; }
public Guid SessionId { private get; set; }
public ApplicationSettings Settings { private get; set; }
public AppSettings Settings { private get; set; }
public AppConfig AppConfig
{
@ -73,36 +71,34 @@ namespace SafeExamBrowser.Client
public ClientController(
IActionCenter actionCenter,
IApplicationMonitor applicationMonitor,
IDisplayMonitor displayMonitor,
IExplorerShell explorerShell,
IHashAlgorithm hashAlgorithm,
ILogger logger,
IMessageBox messageBox,
IOperationSequence operations,
IProcessMonitor processMonitor,
IRuntimeProxy runtime,
Action shutdown,
ITaskbar taskbar,
ITerminationActivator terminationActivator,
IText text,
IUserInterfaceFactory uiFactory,
IWindowMonitor windowMonitor)
IUserInterfaceFactory uiFactory)
{
this.actionCenter = actionCenter;
this.applicationMonitor = applicationMonitor;
this.displayMonitor = displayMonitor;
this.explorerShell = explorerShell;
this.hashAlgorithm = hashAlgorithm;
this.logger = logger;
this.messageBox = messageBox;
this.operations = operations;
this.processMonitor = processMonitor;
this.runtime = runtime;
this.shutdown = shutdown;
this.taskbar = taskbar;
this.terminationActivator = terminationActivator;
this.text = text;
this.uiFactory = uiFactory;
this.windowMonitor = windowMonitor;
}
public bool TryStart()
@ -110,6 +106,7 @@ namespace SafeExamBrowser.Client
logger.Info("Initiating startup procedure...");
splashScreen = uiFactory.CreateSplashScreen();
operations.ActionRequired += Operations_ActionRequired;
operations.ProgressChanged += Operations_ProgressChanged;
operations.StatusChanged += Operations_StatusChanged;
@ -175,28 +172,26 @@ namespace SafeExamBrowser.Client
private void RegisterEvents()
{
actionCenter.QuitButtonClicked += Shell_QuitButtonClicked;
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost += Runtime_ConnectionLost;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
terminationActivator.Activated += TerminationActivator_Activated;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
}
private void DeregisterEvents()
{
actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked;
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost -= Runtime_ConnectionLost;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
terminationActivator.Activated -= TerminationActivator_Activated;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
if (Browser != null)
{
@ -226,6 +221,18 @@ namespace SafeExamBrowser.Client
Browser.Start();
}
private void ApplicationMonitor_ExplorerStarted()
{
logger.Info("Trying to terminate Windows explorer...");
explorerShell.Terminate();
logger.Info("Reinitializing working area...");
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
logger.Info("Reinitializing shell...");
actionCenter.InitializeBounds();
taskbar.InitializeBounds();
logger.Info("Desktop successfully restored.");
}
private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
{
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
@ -339,6 +346,11 @@ namespace SafeExamBrowser.Client
logger.Info("Desktop successfully restored.");
}
private void Operations_ActionRequired(ActionRequiredEventArgs args)
{
// TODO
}
private void Operations_ProgressChanged(ProgressChangedEventArgs args)
{
if (args.CurrentValue.HasValue)
@ -372,18 +384,6 @@ namespace SafeExamBrowser.Client
splashScreen?.UpdateStatus(status, true);
}
private void ProcessMonitor_ExplorerStarted()
{
logger.Info("Trying to terminate Windows explorer...");
explorerShell.Terminate();
logger.Info("Reinitializing working area...");
displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight());
logger.Info("Reinitializing shell...");
actionCenter.InitializeBounds();
taskbar.InitializeBounds();
logger.Info("Desktop successfully restored.");
}
private void Runtime_ConnectionLost()
{
logger.Error("Lost connection to the runtime!");
@ -406,21 +406,6 @@ namespace SafeExamBrowser.Client
terminationActivator.Resume();
}
private void WindowMonitor_WindowChanged(IntPtr window)
{
var allowed = processMonitor.BelongsToAllowedProcess(window);
if (!allowed)
{
var success = windowMonitor.Hide(window);
if (!success)
{
windowMonitor.Close(window);
}
}
}
private bool TryInitiateShutdown()
{
var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash);

View file

@ -23,7 +23,6 @@ using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Communication.Hosts;
using SafeExamBrowser.Communication.Proxies;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.OperationModel;
@ -32,13 +31,13 @@ using SafeExamBrowser.I18n;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Processes;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.Monitoring.Applications;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Display;
using SafeExamBrowser.Monitoring.Keyboard;
using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.Processes;
using SafeExamBrowser.Monitoring.Windows;
using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents;
using SafeExamBrowser.SystemComponents.Audio;
using SafeExamBrowser.SystemComponents.Contracts;
@ -52,7 +51,6 @@ using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Contracts;
using Desktop = SafeExamBrowser.UserInterface.Desktop;
using Mobile = SafeExamBrowser.UserInterface.Mobile;
using SafeExamBrowser.Settings.Logging;
namespace SafeExamBrowser.Client
{
@ -60,17 +58,18 @@ namespace SafeExamBrowser.Client
{
private Guid authenticationToken;
private ClientConfiguration configuration;
private ClientContext context;
private string logFilePath;
private LogLevel logLevel;
private string runtimeHostUri;
private UserInterfaceMode uiMode;
private IActionCenter actionCenter;
private IApplicationMonitor applicationMonitor;
private IBrowserApplication browser;
private IClientHost clientHost;
private ILogger logger;
private IMessageBox messageBox;
private IProcessMonitor processMonitor;
private INativeMethods nativeMethods;
private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo;
@ -79,7 +78,6 @@ namespace SafeExamBrowser.Client
private IText text;
private ITextResource textResource;
private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
internal IClientController ClientController { get; private set; }
@ -96,13 +94,13 @@ namespace SafeExamBrowser.Client
InitializeText();
actionCenter = BuildActionCenter();
applicationMonitor = new ApplicationMonitor(new ModuleLogger(logger, nameof(ApplicationMonitor)), nativeMethods);
context = new ClientContext();
messageBox = BuildMessageBox();
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client);
taskbar = BuildTaskbar();
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator)));
windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods);
var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods, systemInfo);
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods);
@ -112,14 +110,13 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new I18nOperation(logger, text, textResource));
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, authenticationToken));
operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy));
operations.Enqueue(new ConfigurationOperation(configuration, context, logger, runtimeProxy));
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildApplicationOperation));
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
@ -130,20 +127,19 @@ namespace SafeExamBrowser.Client
ClientController = new ClientController(
actionCenter,
applicationMonitor,
displayMonitor,
explorerShell,
hashAlgorithm,
logger,
messageBox,
sequence,
processMonitor,
runtimeProxy,
shutdown,
taskbar,
terminationActivator,
text,
uiFactory,
windowMonitor);
uiFactory);
}
internal void LogStartupInformation()
@ -202,6 +198,11 @@ namespace SafeExamBrowser.Client
textResource = new XmlTextResource(path);
}
private IOperation BuildApplicationOperation()
{
return new ApplicationOperation(applicationMonitor, context, logger);
}
private IOperation BuildBrowserOperation()
{
var moduleLogger = new ModuleLogger(logger, nameof(BrowserApplication));
@ -238,25 +239,20 @@ namespace SafeExamBrowser.Client
private IOperation BuildKeyboardInterceptorOperation()
{
var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor)));
var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods);
var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor)), nativeMethods);
var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger);
return operation;
}
private IOperation BuildMouseInterceptorOperation()
{
var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse);
var operation = new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods);
var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse, nativeMethods);
var operation = new MouseInterceptorOperation(logger, mouseInterceptor);
return operation;
}
private IOperation BuildProcessMonitorOperation()
{
return new ProcessMonitorOperation(logger, processMonitor, configuration.Settings);
}
private IOperation BuildShellOperation()
{
var aboutInfo = new AboutNotificationInfo(text);
@ -295,11 +291,6 @@ namespace SafeExamBrowser.Client
return operation;
}
private IOperation BuildWindowMonitorOperation()
{
return new WindowMonitorOperation(configuration.Settings.KioskMode, logger, windowMonitor);
}
private IActionCenter BuildActionCenter()
{
switch (uiMode)

View file

@ -0,0 +1,136 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Client.Operations.Events;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Client.Operations
{
internal class ApplicationOperation : ClientOperation
{
private ILogger logger;
private IApplicationMonitor applicationMonitor;
public override event ActionRequiredEventHandler ActionRequired;
public override event StatusChangedEventHandler StatusChanged;
public ApplicationOperation(IApplicationMonitor applicationMonitor, ClientContext context, ILogger logger) : base(context)
{
this.applicationMonitor = applicationMonitor;
this.logger = logger;
}
public override OperationResult Perform()
{
logger.Info("Initializing applications...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProcessMonitoring);
var result = InitializeApplications();
if (result == OperationResult.Success)
{
StartMonitor();
}
return result;
}
public override OperationResult Revert()
{
logger.Info("Finalizing applications...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopProcessMonitoring);
TerminateApplications();
StopMonitor();
return OperationResult.Success;
}
private OperationResult InitializeApplications()
{
var initialization = applicationMonitor.Initialize(Context.Settings.Applications);
var result = OperationResult.Success;
if (initialization.RunningApplications.Any())
{
result = TryTerminate(initialization.RunningApplications);
}
if (result == OperationResult.Success)
{
foreach (var application in Context.Settings.Applications.Whitelist)
{
Create(application);
}
}
return result;
}
private void Create(WhitelistApplication application)
{
// TODO: Use IApplicationFactory to create new application according to configuration, load into Context.Applications
}
private void StartMonitor()
{
if (Context.Settings.KioskMode != KioskMode.None)
{
applicationMonitor.Start();
}
}
private void StopMonitor()
{
if (Context.Settings.KioskMode != KioskMode.None)
{
applicationMonitor.Stop();
}
}
private void TerminateApplications()
{
}
private OperationResult TryTerminate(IEnumerable<RunningApplicationInfo> runningApplications)
{
var args = new ProcessTerminationEventArgs();
var result = OperationResult.Success;
ActionRequired?.Invoke(args);
if (args.TerminateProcesses)
{
// TODO: Terminate all processes of all running applications
//foreach (var application in runningApplications)
//{
// foreach (var process in application.Processes)
// {
// process.Kill();
// }
//}
}
else
{
result = OperationResult.Aborted;
}
return result;
}
}
}

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2019 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;
namespace SafeExamBrowser.Client.Operations
{
/// <summary>
/// The base implementation to be used for all operations in the client operation sequence.
/// </summary>
internal abstract class ClientOperation : IOperation
{
protected ClientContext Context { get; private set; }
public abstract event ActionRequiredEventHandler ActionRequired;
public abstract event StatusChangedEventHandler StatusChanged;
public ClientOperation(ClientContext context)
{
Context = context;
}
public abstract OperationResult Perform();
public abstract OperationResult Revert();
}
}

View file

@ -15,23 +15,24 @@ using SafeExamBrowser.Logging.Contracts;
namespace SafeExamBrowser.Client.Operations
{
internal class ConfigurationOperation : IOperation
internal class ConfigurationOperation : ClientOperation
{
private ClientConfiguration configuration;
private ILogger logger;
private IRuntimeProxy runtime;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public override event StatusChangedEventHandler StatusChanged;
public ConfigurationOperation(ClientConfiguration configuration, ILogger logger, IRuntimeProxy runtime)
// TODO: Remove and delete ClientConfiguration!
public ConfigurationOperation(ClientConfiguration configuration, ClientContext context, ILogger logger, IRuntimeProxy runtime) : base(context)
{
this.configuration = configuration;
this.logger = logger;
this.runtime = runtime;
}
public OperationResult Perform()
public override OperationResult Perform()
{
logger.Info("Initializing application configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
@ -43,6 +44,9 @@ namespace SafeExamBrowser.Client.Operations
configuration.SessionId = config.SessionId;
configuration.Settings = config.Settings;
Context.AppConfig = config.AppConfig;
Context.Settings = config.Settings;
logger.Info("Successfully retrieved the application configuration from the runtime.");
logger.Info($" -> Client-ID: {configuration.AppConfig.ClientId}");
logger.Info($" -> Runtime-ID: {configuration.AppConfig.RuntimeId}");
@ -51,7 +55,7 @@ namespace SafeExamBrowser.Client.Operations
return OperationResult.Success;
}
public OperationResult Revert()
public override OperationResult Revert()
{
return OperationResult.Success;
}

View file

@ -0,0 +1,17 @@
/*
* Copyright (c) 2019 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.Events;
namespace SafeExamBrowser.Client.Operations.Events
{
internal class ProcessTerminationEventArgs : ActionRequiredEventArgs
{
public bool TerminateProcesses { get; set; }
}
}

View file

@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.Operations
{
@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations
{
private IKeyboardInterceptor keyboardInterceptor;
private ILogger logger;
private INativeMethods nativeMethods;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public KeyboardInterceptorOperation(
IKeyboardInterceptor keyboardInterceptor,
ILogger logger,
INativeMethods nativeMethods)
public KeyboardInterceptorOperation(IKeyboardInterceptor keyboardInterceptor, ILogger logger)
{
this.keyboardInterceptor = keyboardInterceptor;
this.logger = logger;
this.nativeMethods = nativeMethods;
}
public OperationResult Perform()
@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Starting keyboard interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StartKeyboardInterception);
nativeMethods.RegisterKeyboardHook(keyboardInterceptor);
keyboardInterceptor.Start();
return OperationResult.Success;
}
@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Stopping keyboard interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception);
nativeMethods.DeregisterKeyboardHook(keyboardInterceptor);
keyboardInterceptor.Stop();
return OperationResult.Success;
}

View file

@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.Operations
{
@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations
{
private ILogger logger;
private IMouseInterceptor mouseInterceptor;
private INativeMethods nativeMethods;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public MouseInterceptorOperation(
ILogger logger,
IMouseInterceptor mouseInterceptor,
INativeMethods nativeMethods)
public MouseInterceptorOperation(ILogger logger, IMouseInterceptor mouseInterceptor)
{
this.logger = logger;
this.mouseInterceptor = mouseInterceptor;
this.nativeMethods = nativeMethods;
}
public OperationResult Perform()
@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Starting mouse interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StartMouseInterception);
nativeMethods.RegisterMouseHook(mouseInterceptor);
mouseInterceptor.Start();
return OperationResult.Success;
}
@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Stopping mouse interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception);
nativeMethods.DeregisterMouseHook(mouseInterceptor);
mouseInterceptor.Stop();
return OperationResult.Success;
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (c) 2019 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.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Processes;
namespace SafeExamBrowser.Client.Operations
{
internal class ProcessMonitorOperation : IOperation
{
private ILogger logger;
private IProcessMonitor processMonitor;
private ApplicationSettings settings;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public ProcessMonitorOperation(ILogger logger, IProcessMonitor processMonitor, ApplicationSettings settings)
{
this.logger = logger;
this.processMonitor = processMonitor;
this.settings = settings;
}
public OperationResult Perform()
{
logger.Info("Initializing process monitoring...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeProcessMonitoring);
if (settings.KioskMode == KioskMode.DisableExplorerShell)
{
processMonitor.StartMonitoringExplorer();
}
return OperationResult.Success;
}
public OperationResult Revert()
{
logger.Info("Stopping process monitoring...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopProcessMonitoring);
if (settings.KioskMode == KioskMode.DisableExplorerShell)
{
processMonitor.StopMonitoringExplorer();
}
return OperationResult.Success;
}
}
}

View file

@ -1,60 +0,0 @@
/*
* Copyright (c) 2019 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.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Windows;
namespace SafeExamBrowser.Client.Operations
{
internal class WindowMonitorOperation : IOperation
{
private KioskMode kioskMode;
private ILogger logger;
private IWindowMonitor windowMonitor;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged;
public WindowMonitorOperation(KioskMode kioskMode, ILogger logger, IWindowMonitor windowMonitor)
{
this.kioskMode = kioskMode;
this.logger = logger;
this.windowMonitor = windowMonitor;
}
public OperationResult Perform()
{
logger.Info("Initializing window monitoring...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWindowMonitoring);
if (kioskMode != KioskMode.None)
{
windowMonitor.StartMonitoringWindows();
}
return OperationResult.Success;
}
public OperationResult Revert()
{
logger.Info("Stopping window monitoring...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopWindowMonitoring);
if (kioskMode != KioskMode.None)
{
windowMonitor.StopMonitoringWindows();
}
return OperationResult.Success;
}
}
}

View file

@ -71,9 +71,12 @@
</ItemGroup>
<ItemGroup>
<Compile Include="App.cs" />
<Compile Include="ClientContext.cs" />
<Compile Include="ClientController.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
<Compile Include="Operations\ClientOperation.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\Events\ProcessTerminationEventArgs.cs" />
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
<Compile Include="Communication\ClientHost.cs" />
<Compile Include="CompositionRoot.cs" />
@ -88,9 +91,8 @@
<Compile Include="Operations\DisplayMonitorOperation.cs" />
<Compile Include="Operations\KeyboardInterceptorOperation.cs" />
<Compile Include="Operations\MouseInterceptorOperation.cs" />
<Compile Include="Operations\ProcessMonitorOperation.cs" />
<Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\WindowMonitorOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
@ -220,6 +222,7 @@
<ItemGroup>
<Resource Include="SafeExamBrowser.ico" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup>
<PostBuildEvent>robocopy "$(SolutionDir)SafeExamBrowser.Browser\bin\$(PlatformName)\$(ConfigurationName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)" /e /np

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts
{
@ -29,6 +30,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary>
/// The application settings to be used by the client.
/// </summary>
public Settings.ApplicationSettings Settings { get; set; }
public AppSettings Settings { get; set; }
}
}

View file

@ -10,6 +10,7 @@ using System;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Configuration.Contracts.DataResources;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts
{
@ -36,7 +37,7 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary>
/// Loads the default settings.
/// </summary>
Settings.ApplicationSettings LoadDefaultSettings();
AppSettings LoadDefaultSettings();
/// <summary>
/// Registers the specified <see cref="IDataParser"/> to be used to parse configuration data.
@ -61,6 +62,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary>
/// Attempts to load settings from the specified resource.
/// </summary>
LoadStatus TryLoadSettings(Uri resource, out Settings.ApplicationSettings settings, PasswordParameters password = null);
LoadStatus TryLoadSettings(Uri resource, out AppSettings settings, PasswordParameters password = null);
}
}

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts
{
@ -29,7 +30,7 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary>
/// The application settings to be used by the service.
/// </summary>
public Settings.ApplicationSettings Settings { get; set; }
public AppSettings Settings { get; set; }
/// <summary>
/// The user name of the currently logged in user.

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts
{
@ -16,7 +17,7 @@ namespace SafeExamBrowser.Configuration.Contracts
public class SessionConfiguration
{
/// <summary>
/// The active application configuration for this session.
/// The application configuration for this session.
/// </summary>
public AppConfig AppConfig { get; set; }
@ -33,6 +34,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary>
/// The settings used for this session.
/// </summary>
public Settings.ApplicationSettings Settings { get; set; }
public AppSettings Settings { get; set; }
}
}

View file

@ -0,0 +1,74 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapApplicationBlacklist(AppSettings settings, object value)
{
if (value is IList<object> applications)
{
foreach (var item in applications)
{
if (item is IDictionary<string, object> applicationData)
{
var isActive = applicationData.TryGetValue(Keys.Applications.ApplicationActive, out var v) && v is bool active && active;
var isWindowsProcess = applicationData.TryGetValue(Keys.Applications.ApplicationOs, out v) && v is int os && os == Keys.WINDOWS;
if (isActive && isWindowsProcess)
{
var application = new BlacklistApplication();
if (applicationData.TryGetValue(Keys.Applications.ApplicationAutoTerminate, out v) && v is bool autoTerminate)
{
application.AutoTerminate = autoTerminate;
}
if (applicationData.TryGetValue(Keys.Applications.ApplicationExecutable, out v) && v is string executableName)
{
application.ExecutableName = executableName;
}
if (applicationData.TryGetValue(Keys.Applications.ApplicationOriginalName, out v) && v is string originalName)
{
application.ExecutableOriginalName = originalName;
}
settings.Applications.Blacklist.Add(application);
}
}
}
}
}
private void MapApplicationWhitelist(AppSettings settings, object value)
{
if (value is IList<object> applications)
{
foreach (var item in applications)
{
if (item is IDictionary<string, object> application)
{
var isActive = application.TryGetValue(Keys.Applications.ApplicationActive, out var v) && v is bool active && active;
var isWindowsProcess = application.TryGetValue(Keys.Applications.ApplicationOs, out v) && v is int os && os == Keys.WINDOWS;
if (isActive && isWindowsProcess)
{
}
}
}
}
}
}
}

View file

@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapInitialVolumeLevel(ApplicationSettings settings, object value)
private void MapInitialVolumeLevel(AppSettings settings, object value)
{
if (value is int volume)
{
@ -20,7 +20,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapMuteAudio(ApplicationSettings settings, object value)
private void MapMuteAudio(AppSettings settings, object value)
{
if (value is bool mute)
{
@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapSetInitialVolumeLevel(ApplicationSettings settings, object value)
private void MapSetInitialVolumeLevel(AppSettings settings, object value)
{
if (value is bool initialize)
{

View file

@ -15,7 +15,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapAllowAddressBar(ApplicationSettings settings, object value)
private void MapAllowAddressBar(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -23,7 +23,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowAddressBarAdditionalWindow(ApplicationSettings settings, object value)
private void MapAllowAddressBarAdditionalWindow(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -31,7 +31,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowConfigurationDownloads(ApplicationSettings settings, object value)
private void MapAllowConfigurationDownloads(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -39,7 +39,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowDeveloperConsole(ApplicationSettings settings, object value)
private void MapAllowDeveloperConsole(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -48,7 +48,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowDownloads(ApplicationSettings settings, object value)
private void MapAllowDownloads(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -56,7 +56,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowNavigation(ApplicationSettings settings, object value)
private void MapAllowNavigation(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -65,7 +65,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowNavigationAdditionalWindow(ApplicationSettings settings, object value)
private void MapAllowNavigationAdditionalWindow(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -74,7 +74,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowPageZoom(ApplicationSettings settings, object value)
private void MapAllowPageZoom(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -82,7 +82,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowPopups(ApplicationSettings settings, object value)
private void MapAllowPopups(AppSettings settings, object value)
{
if (value is bool block)
{
@ -90,7 +90,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowReload(ApplicationSettings settings, object value)
private void MapAllowReload(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -98,7 +98,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAllowReloadAdditionalWindow(ApplicationSettings settings, object value)
private void MapAllowReloadAdditionalWindow(AppSettings settings, object value)
{
if (value is bool allow)
{
@ -106,7 +106,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableContentRequestFilter(ApplicationSettings settings, object value)
private void MapEnableContentRequestFilter(AppSettings settings, object value)
{
if (value is bool process)
{
@ -114,7 +114,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableMainRequestFilter(ApplicationSettings settings, object value)
private void MapEnableMainRequestFilter(AppSettings settings, object value)
{
if (value is bool process)
{
@ -122,7 +122,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapUrlFilterRules(ApplicationSettings settings, object value)
private void MapUrlFilterRules(AppSettings settings, object value)
{
const int ALLOW = 1;
@ -132,7 +132,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
if (item is IDictionary<string, object> ruleData)
{
if (ruleData.TryGetValue(Keys.Browser.Filter.RuleIsActive, out var v) && v is bool active && active)
var isActive = ruleData.TryGetValue(Keys.Browser.Filter.RuleIsActive, out var v) && v is bool active && active;
if (isActive)
{
var rule = new FilterRuleSettings();
@ -158,7 +160,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapMainWindowMode(ApplicationSettings settings, object value)
private void MapMainWindowMode(AppSettings settings, object value)
{
const int FULLSCREEN = 1;
@ -168,7 +170,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapShowReloadWarning(ApplicationSettings settings, object value)
private void MapShowReloadWarning(AppSettings settings, object value)
{
if (value is bool show)
{
@ -176,7 +178,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapShowReloadWarningAdditionalWindow(ApplicationSettings settings, object value)
private void MapShowReloadWarningAdditionalWindow(AppSettings settings, object value)
{
if (value is bool show)
{
@ -184,7 +186,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapUserAgentMode(IDictionary<string, object> rawData, ApplicationSettings settings)
private void MapUserAgentMode(IDictionary<string, object> rawData, AppSettings settings)
{
const int DEFAULT = 0;

View file

@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapConfigurationMode(ApplicationSettings settings, object value)
private void MapConfigurationMode(AppSettings settings, object value)
{
const int CONFIGURE_CLIENT = 1;

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapAdminPasswordHash(ApplicationSettings settings, object value)
private void MapAdminPasswordHash(AppSettings settings, object value)
{
if (value is string hash)
{
@ -22,7 +22,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapApplicationLogAccess(IDictionary<string, object> rawData, ApplicationSettings settings)
private void MapApplicationLogAccess(IDictionary<string, object> rawData, AppSettings settings)
{
var hasValue = rawData.TryGetValue(Keys.General.AllowApplicationLog, out var value);
@ -42,7 +42,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapLogLevel(ApplicationSettings settings, object value)
private void MapLogLevel(AppSettings settings, object value)
{
const int ERROR = 0, WARNING = 1, INFO = 2;
@ -52,7 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapQuitPasswordHash(ApplicationSettings settings, object value)
private void MapQuitPasswordHash(AppSettings settings, object value)
{
if (value is string hash)
{
@ -60,7 +60,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapStartUrl(ApplicationSettings settings, object value)
private void MapStartUrl(AppSettings settings, object value)
{
if (value is string url)
{

View file

@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapEnableAltEsc(ApplicationSettings settings, object value)
private void MapEnableAltEsc(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -20,7 +20,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableAltF4(ApplicationSettings settings, object value)
private void MapEnableAltF4(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableAltTab(ApplicationSettings settings, object value)
private void MapEnableAltTab(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -36,7 +36,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableCtrlEsc(ApplicationSettings settings, object value)
private void MapEnableCtrlEsc(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -44,7 +44,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableEsc(ApplicationSettings settings, object value)
private void MapEnableEsc(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -52,7 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF1(ApplicationSettings settings, object value)
private void MapEnableF1(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -60,7 +60,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF2(ApplicationSettings settings, object value)
private void MapEnableF2(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -68,7 +68,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF3(ApplicationSettings settings, object value)
private void MapEnableF3(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -76,7 +76,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF4(ApplicationSettings settings, object value)
private void MapEnableF4(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -84,7 +84,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF5(ApplicationSettings settings, object value)
private void MapEnableF5(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -92,7 +92,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF6(ApplicationSettings settings, object value)
private void MapEnableF6(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -100,7 +100,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF7(ApplicationSettings settings, object value)
private void MapEnableF7(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -108,7 +108,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF8(ApplicationSettings settings, object value)
private void MapEnableF8(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -116,7 +116,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF9(ApplicationSettings settings, object value)
private void MapEnableF9(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -124,7 +124,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF10(ApplicationSettings settings, object value)
private void MapEnableF10(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -132,7 +132,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF11(ApplicationSettings settings, object value)
private void MapEnableF11(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -140,7 +140,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableF12(ApplicationSettings settings, object value)
private void MapEnableF12(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -148,7 +148,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnablePrintScreen(ApplicationSettings settings, object value)
private void MapEnablePrintScreen(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -156,7 +156,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableSystemKey(ApplicationSettings settings, object value)
private void MapEnableSystemKey(AppSettings settings, object value)
{
if (value is bool enabled)
{
@ -164,7 +164,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapEnableRightMouse(ApplicationSettings settings, object value)
private void MapEnableRightMouse(AppSettings settings, object value)
{
if (value is bool enabled)
{

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapKioskMode(IDictionary<string, object> rawData, ApplicationSettings settings)
private void MapKioskMode(IDictionary<string, object> rawData, AppSettings settings)
{
var hasCreateNewDesktop = rawData.TryGetValue(Keys.Security.KioskModeCreateNewDesktop, out var createNewDesktop);
var hasDisableExplorerShell = rawData.TryGetValue(Keys.Security.KioskModeDisableExplorerShell, out var disableExplorerShell);
@ -35,7 +35,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapServicePolicy(ApplicationSettings settings, object value)
private void MapServicePolicy(AppSettings settings, object value)
{
const int WARN = 1;
const int FORCE = 2;

View file

@ -13,7 +13,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
private void MapApplicationLogButton(ApplicationSettings settings, object value)
private void MapApplicationLogButton(AppSettings settings, object value)
{
if (value is bool show)
{
@ -21,7 +21,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapAudio(ApplicationSettings settings, object value)
private void MapAudio(AppSettings settings, object value)
{
if (value is bool show)
{
@ -30,7 +30,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapClock(ApplicationSettings settings, object value)
private void MapClock(AppSettings settings, object value)
{
if (value is bool show)
{
@ -39,7 +39,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapKeyboardLayout(ApplicationSettings settings, object value)
private void MapKeyboardLayout(AppSettings settings, object value)
{
if (value is bool show)
{
@ -48,7 +48,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapWirelessNetwork(ApplicationSettings settings, object value)
private void MapWirelessNetwork(AppSettings settings, object value)
{
if (value is bool show)
{
@ -57,7 +57,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapUserInterfaceMode(ApplicationSettings settings, object value)
private void MapUserInterfaceMode(AppSettings settings, object value)
{
if (value is bool mobile)
{

View file

@ -13,10 +13,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal partial class DataMapper
{
internal void MapRawDataToSettings(IDictionary<string, object> rawData, ApplicationSettings settings)
internal void MapRawDataToSettings(IDictionary<string, object> rawData, AppSettings settings)
{
foreach (var item in rawData)
{
MapApplicationSettings(item.Key, item.Value, settings);
MapAudioSettings(item.Key, item.Value, settings);
MapBrowserSettings(item.Key, item.Value, settings);
MapConfigurationFileSettings(item.Key, item.Value, settings);
@ -31,7 +32,20 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
MapUserAgentMode(rawData, settings);
}
private void MapAudioSettings(string key, object value, ApplicationSettings settings)
private void MapApplicationSettings(string key, object value, AppSettings settings)
{
switch (key)
{
case Keys.Applications.Blacklist:
MapApplicationBlacklist(settings, value);
break;
case Keys.Applications.Whitelist:
MapApplicationWhitelist(settings, value);
break;
}
}
private void MapAudioSettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -47,7 +61,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapBrowserSettings(string key, object value, ApplicationSettings settings)
private void MapBrowserSettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -105,7 +119,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapConfigurationFileSettings(string key, object value, ApplicationSettings settings)
private void MapConfigurationFileSettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -115,7 +129,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapGeneralSettings(string key, object value, ApplicationSettings settings)
private void MapGeneralSettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -134,7 +148,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapInputSettings(string key, object value, ApplicationSettings settings)
private void MapInputSettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -201,7 +215,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapSecuritySettings(string key, object value, ApplicationSettings settings)
private void MapSecuritySettings(string key, object value, AppSettings settings)
{
switch (key)
{
@ -211,7 +225,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
}
}
private void MapUserInterfaceSettings(string key, object value, ApplicationSettings settings)
private void MapUserInterfaceSettings(string key, object value, AppSettings settings)
{
switch (key)
{

View file

@ -99,9 +99,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
return configuration;
}
internal ApplicationSettings LoadDefaultSettings()
internal AppSettings LoadDefaultSettings()
{
var settings = new ApplicationSettings();
var settings = new AppSettings();
settings.ActionCenter.EnableActionCenter = true;
settings.ActionCenter.ShowApplicationInfo = true;

View file

@ -10,12 +10,21 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{
internal static class Keys
{
internal const int WINDOWS = 1;
internal static class AdditionalResources
{
}
internal static class Applications
{
internal const string ApplicationActive = "active";
internal const string ApplicationAutoTerminate = "strongKill";
internal const string ApplicationExecutable = "executable";
internal const string ApplicationOriginalName = "originalName";
internal const string ApplicationOs = "os";
internal const string Blacklist = "prohibitedProcesses";
internal const string Whitelist = "permittedProcesses";
}
internal static class Audio

View file

@ -100,7 +100,7 @@ namespace SafeExamBrowser.Configuration
return dataValues.InitializeSessionConfiguration();
}
public ApplicationSettings LoadDefaultSettings()
public AppSettings LoadDefaultSettings()
{
return dataValues.LoadDefaultSettings();
}
@ -125,7 +125,7 @@ namespace SafeExamBrowser.Configuration
resourceSavers.Add(saver);
}
public LoadStatus TryLoadSettings(Uri resource, out ApplicationSettings settings, PasswordParameters password = null)
public LoadStatus TryLoadSettings(Uri resource, out AppSettings settings, PasswordParameters password = null)
{
logger.Info($"Attempting to load '{resource}'...");

View file

@ -66,6 +66,9 @@
<Compile Include="DataFormats\BinarySerializer.cs" />
<Compile Include="DataFormats\BinaryBlock.cs" />
<Compile Include="ConfigurationData\DataMapper.cs" />
<Compile Include="ConfigurationData\DataMapper.Applications.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>
<Compile Include="ConfigurationData\DataMapper.Audio.cs">
<DependentUpon>DataMapper.cs</DependentUpon>
</Compile>

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Monitoring.Contracts.Processes.Events
namespace SafeExamBrowser.Monitoring.Contracts.Applications.Events
{
/// <summary>
/// Indicates that the Windows explorer process has started.

View file

@ -0,0 +1,39 @@
/*
* Copyright (c) 2019 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.Applications.Events;
using SafeExamBrowser.Settings.Applications;
namespace SafeExamBrowser.Monitoring.Contracts.Applications
{
/// <summary>
/// Monitors applications running on the computer.
/// </summary>
public interface IApplicationMonitor
{
/// <summary>
/// Event fired when a new instance of the Windows Explorer has been started.
/// </summary>
event ExplorerStartedEventHandler ExplorerStarted;
/// <summary>
/// Initializes the application monitor.
/// </summary>
InitializationResult Initialize(ApplicationSettings settings);
/// <summary>
/// Starts monitoring all initialized applications. Windows Explorer will always be monitored.
/// </summary>
void Start();
/// <summary>
/// Stops the application monitoring.
/// </summary>
void Stop();
}
}

View file

@ -0,0 +1,28 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
namespace SafeExamBrowser.Monitoring.Contracts.Applications
{
/// <summary>
/// Provides information about the initialization of the <see cref="IApplicationMonitor"/>.
/// </summary>
public class InitializationResult
{
/// <summary>
/// A list of currently running applications which need to be terminated.
/// </summary>
public IEnumerable<RunningApplicationInfo> RunningApplications { get; }
public InitializationResult()
{
RunningApplications = new List<RunningApplicationInfo>();
}
}
}

View file

@ -0,0 +1,29 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Monitoring.Contracts.Applications
{
/// <summary>
/// Provides information about a running application.
/// </summary>
public class RunningApplicationInfo
{
/// <summary>
/// The name of the application.
/// </summary>
public string Name { get; set; }
/// <summary>
/// A list of processes which belong to the application.
/// </summary>
public IEnumerable<IProcess> Processes { get; set; }
}
}

View file

@ -14,8 +14,13 @@ namespace SafeExamBrowser.Monitoring.Contracts.Keyboard
public interface IKeyboardInterceptor
{
/// <summary>
/// Returns <c>true</c> if the given key should be blocked, otherwise <c>false</c>. The key code corresponds to a Win32 Virtual-Key.
/// Starts intercepting keyboard input.
/// </summary>
bool Block(int keyCode, KeyModifier modifier, KeyState state);
void Start();
/// <summary>
/// Stops intercepting keyboard input.
/// </summary>
void Stop();
}
}

View file

@ -14,8 +14,13 @@ namespace SafeExamBrowser.Monitoring.Contracts.Mouse
public interface IMouseInterceptor
{
/// <summary>
/// Returns <c>true</c> if the given button should be blocked, otherwise <c>false</c>.
/// Starts intercepting mouse input.
/// </summary>
bool Block(MouseButton button, MouseButtonState state);
void Start();
/// <summary>
/// Stops intercepting mouse input.
/// </summary>
void Stop();
}
}

View file

@ -1,40 +0,0 @@
/*
* Copyright (c) 2019 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 SafeExamBrowser.Monitoring.Contracts.Processes.Events;
namespace SafeExamBrowser.Monitoring.Contracts.Processes
{
/// <summary>
/// Monitors the processes running on the computer and provides access to process-related functionality.
/// </summary>
public interface IProcessMonitor
{
/// <summary>
/// Event fired when the process monitor observes that a new instance of the Windows explorer has been started.
/// </summary>
event ExplorerStartedEventHandler ExplorerStarted;
/// <summary>
/// Performs a check whether the process associated to the given window is allowed.
/// </summary>
bool BelongsToAllowedProcess(IntPtr window);
/// <summary>
/// Starts monitoring the Windows explorer, i.e. any newly created instances of <c>explorer.exe</c> will trigger the
/// <see cref="ExplorerStarted"/> event.
/// </summary>
void StartMonitoringExplorer();
/// <summary>
/// Stops monitoring the Windows explorer.
/// </summary>
void StopMonitoringExplorer();
}
}

View file

@ -53,19 +53,25 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Applications\RunningApplicationInfo.cs" />
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
<Compile Include="Processes\Events\ExplorerStartedEventHandler.cs" />
<Compile Include="Windows\Events\WindowChangedEventHandler.cs" />
<Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
<Compile Include="Display\IDisplayMonitor.cs" />
<Compile Include="Keyboard\IKeyboardInterceptor.cs" />
<Compile Include="Mouse\MouseButtonState.cs" />
<Compile Include="Mouse\IMouseInterceptor.cs" />
<Compile Include="Processes\IProcessMonitor.cs" />
<Compile Include="Windows\IWindowMonitor.cs" />
<Compile Include="Keyboard\KeyModifier.cs" />
<Compile Include="Keyboard\KeyState.cs" />
<Compile Include="Mouse\MouseButton.cs" />
<Compile Include="Applications\InitializationResult.cs" />
<Compile Include="Applications\IApplicationMonitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
<Project>{30b2d907-5861-4f39-abad-c4abf1b3470e}</Project>
<Name>SafeExamBrowser.Settings</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.WindowsApi.Contracts\SafeExamBrowser.WindowsApi.Contracts.csproj">
<Project>{7016f080-9aa5-41b2-a225-385ad877c171}</Project>
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -1,45 +0,0 @@
/*
* Copyright (c) 2019 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 SafeExamBrowser.Monitoring.Contracts.Windows.Events;
namespace SafeExamBrowser.Monitoring.Contracts.Windows
{
/// <summary>
/// Monitors the windows associated with the current desktop and provides window-related functionality.
/// </summary>
public interface IWindowMonitor
{
/// <summary>
/// Event fired when the window monitor observes that the foreground window has changed.
/// </summary>
event WindowChangedEventHandler WindowChanged;
/// <summary>
/// Forcefully closes the specified window.
/// </summary>
void Close(IntPtr window);
/// <summary>
/// Hides the specified window. Returns <c>true</c> if the window was successfully hidden, otherwise <c>false</c>.
/// </summary>
bool Hide(IntPtr window);
/// <summary>
/// Starts monitoring application windows by subscribing to specific system events.
/// If a window is shown which is not supposed to do so, it will be automatically hidden.
/// </summary>
void StartMonitoringWindows();
/// <summary>
/// Stops monitoring windows and deregisters from any subscribed system events.
/// </summary>
void StopMonitoringWindows();
}
}

View file

@ -0,0 +1,177 @@
/*
* Copyright (c) 2019 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.Diagnostics;
using System.Management;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Applications.Events;
using SafeExamBrowser.Settings.Applications;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Monitoring.Applications
{
public class ApplicationMonitor : IApplicationMonitor
{
private IntPtr activeWindow;
private Guid? captureHookId;
private Guid? foregroundHookId;
private ILogger logger;
private INativeMethods nativeMethods;
private ManagementEventWatcher explorerWatcher;
public event ExplorerStartedEventHandler ExplorerStarted;
public ApplicationMonitor(ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
}
public InitializationResult Initialize(ApplicationSettings settings)
{
// TODO
// Initialize blacklist
// Initialize whitelist
// Check for running processes
return new InitializationResult();
}
public void Start()
{
// TODO: Start monitoring blacklist...
explorerWatcher = new ManagementEventWatcher(@"\\.\root\CIMV2", GetQueryFor("explorer.exe"));
explorerWatcher.EventArrived += new EventArrivedEventHandler(ExplorerWatcher_EventArrived);
explorerWatcher.Start();
logger.Info("Started monitoring process 'explorer.exe'.");
captureHookId = nativeMethods.RegisterSystemCaptureStartEvent(SystemEvent_WindowChanged);
logger.Info($"Registered system capture start event with ID = {captureHookId}.");
foregroundHookId = nativeMethods.RegisterSystemForegroundEvent(SystemEvent_WindowChanged);
logger.Info($"Registered system foreground event with ID = {foregroundHookId}.");
}
public void Stop()
{
explorerWatcher?.Stop();
logger.Info("Stopped monitoring 'explorer.exe'.");
if (captureHookId.HasValue)
{
nativeMethods.DeregisterSystemEventHook(captureHookId.Value);
logger.Info($"Unregistered system capture start event with ID = {captureHookId}.");
}
if (foregroundHookId.HasValue)
{
nativeMethods.DeregisterSystemEventHook(foregroundHookId.Value);
logger.Info($"Unregistered system foreground event with ID = {foregroundHookId}.");
}
}
public bool Terminate(int processId)
{
return false;
}
private void Check(IntPtr window)
{
var allowed = IsAllowed(window);
if (!allowed)
{
var success = TryHide(window);
if (!success)
{
Close(window);
}
}
}
private void Close(IntPtr window)
{
var title = nativeMethods.GetWindowTitle(window);
nativeMethods.SendCloseMessageTo(window);
logger.Info($"Sent close message to window '{title}' with handle = {window}.");
}
private bool IsAllowed(IntPtr window)
{
var processId = nativeMethods.GetProcessIdFor(window);
var process = Process.GetProcessById(Convert.ToInt32(processId));
if (process != null)
{
var allowed = process.ProcessName == "SafeExamBrowser" || process.ProcessName == "SafeExamBrowser.Client";
if (!allowed)
{
logger.Warn($"Window with handle = {window} belongs to not allowed process '{process.ProcessName}'!");
}
return allowed;
}
return true;
}
private bool TryHide(IntPtr window)
{
var title = nativeMethods.GetWindowTitle(window);
var success = nativeMethods.HideWindow(window);
if (success)
{
logger.Info($"Hid window '{title}' with handle = {window}.");
}
else
{
logger.Warn($"Failed to hide window '{title}' with handle = {window}!");
}
return success;
}
private void ExplorerWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
var eventName = e.NewEvent.ClassPath.ClassName;
if (eventName == "__InstanceCreationEvent")
{
logger.Warn("A new instance of Windows explorer has been started!");
ExplorerStarted?.Invoke();
}
}
private void SystemEvent_WindowChanged(IntPtr window)
{
if (window != IntPtr.Zero && activeWindow != window)
{
logger.Debug($"Window has changed from {activeWindow} to {window}.");
activeWindow = window;
Check(window);
}
}
private string GetQueryFor(string processName)
{
return $@"
SELECT *
FROM __InstanceOperationEvent
WITHIN 2
WHERE TargetInstance ISA 'Win32_Process'
AND TargetInstance.Name = '{processName}'";
}
}
}

View file

@ -9,24 +9,42 @@
using System;
using System.Linq;
using System.Windows.Input;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.Monitoring.Keyboard
{
public class KeyboardInterceptor : IKeyboardInterceptor
{
private KeyboardSettings settings;
private Guid? hookId;
private ILogger logger;
private INativeMethods nativeMethods;
private KeyboardSettings settings;
public KeyboardInterceptor(KeyboardSettings settings, ILogger logger)
public KeyboardInterceptor(KeyboardSettings settings, ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
this.settings = settings;
}
public bool Block(int keyCode, KeyModifier modifier, KeyState state)
public void Start()
{
hookId = nativeMethods.RegisterKeyboardHook(KeyboardHookCallback);
}
public void Stop()
{
if (hookId.HasValue)
{
nativeMethods.DeregisterKeyboardHook(hookId.Value);
}
}
private bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state)
{
var block = false;
var key = KeyInterop.KeyFromVirtualKey(keyCode);

View file

@ -6,24 +6,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Settings.Monitoring;
using System;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.Monitoring.Mouse
{
public class MouseInterceptor : IMouseInterceptor
{
private Guid? hookId;
private ILogger logger;
private INativeMethods nativeMethods;
private MouseSettings settings;
public MouseInterceptor(ILogger logger, MouseSettings settings)
public MouseInterceptor(ILogger logger, MouseSettings settings, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
this.settings = settings;
}
public bool Block(MouseButton button, MouseButtonState state)
public void Start()
{
hookId = nativeMethods.RegisterMouseHook(MouseHookCallback);
}
public void Stop()
{
if (hookId.HasValue)
{
nativeMethods.DeregisterMouseHook(hookId.Value);
}
}
private bool MouseHookCallback(MouseButton button, MouseButtonState state)
{
var block = false;

View file

@ -1,89 +0,0 @@
/*
* Copyright (c) 2019 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.Diagnostics;
using System.Management;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Processes;
using SafeExamBrowser.Monitoring.Contracts.Processes.Events;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Monitoring.Processes
{
public class ProcessMonitor : IProcessMonitor
{
private ILogger logger;
private INativeMethods nativeMethods;
private ManagementEventWatcher explorerWatcher;
public event ExplorerStartedEventHandler ExplorerStarted;
public ProcessMonitor(ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
}
public bool BelongsToAllowedProcess(IntPtr window)
{
var processId = nativeMethods.GetProcessIdFor(window);
var process = Process.GetProcessById(Convert.ToInt32(processId));
if (process != null)
{
var allowed = process.ProcessName == "SafeExamBrowser" || process.ProcessName == "SafeExamBrowser.Client";
if (!allowed)
{
logger.Warn($"Window with handle = {window} belongs to not allowed process '{process.ProcessName}'!");
}
return allowed;
}
return true;
}
public void StartMonitoringExplorer()
{
explorerWatcher = new ManagementEventWatcher(@"\\.\root\CIMV2", GetQueryFor("explorer.exe"));
explorerWatcher.EventArrived += new EventArrivedEventHandler(ExplorerWatcher_EventArrived);
explorerWatcher.Start();
logger.Info("Started monitoring process 'explorer.exe'.");
}
public void StopMonitoringExplorer()
{
explorerWatcher?.Stop();
logger.Info("Stopped monitoring 'explorer.exe'.");
}
private void ExplorerWatcher_EventArrived(object sender, EventArrivedEventArgs e)
{
var eventName = e.NewEvent.ClassPath.ClassName;
if (eventName == "__InstanceCreationEvent")
{
logger.Warn("A new instance of Windows explorer has been started!");
ExplorerStarted?.Invoke();
}
}
private string GetQueryFor(string processName)
{
return $@"
SELECT *
FROM __InstanceOperationEvent
WITHIN 2
WHERE TargetInstance ISA 'Win32_Process'
AND TargetInstance.Name = '{processName}'";
}
}
}

View file

@ -60,9 +60,8 @@
<Compile Include="Display\DisplayMonitor.cs" />
<Compile Include="Keyboard\KeyboardInterceptor.cs" />
<Compile Include="Mouse\MouseInterceptor.cs" />
<Compile Include="Processes\ProcessMonitor.cs" />
<Compile Include="Applications\ApplicationMonitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Windows\WindowMonitor.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">

View file

@ -1,92 +0,0 @@
/*
* Copyright (c) 2019 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 SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.Monitoring.Contracts.Windows.Events;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Monitoring.Windows
{
public class WindowMonitor : IWindowMonitor
{
private IntPtr activeWindow;
private Guid? captureHookId;
private Guid? foregroundHookId;
private ILogger logger;
private INativeMethods nativeMethods;
public event WindowChangedEventHandler WindowChanged;
public WindowMonitor(ILogger logger, INativeMethods nativeMethods)
{
this.logger = logger;
this.nativeMethods = nativeMethods;
}
public void Close(IntPtr window)
{
var title = nativeMethods.GetWindowTitle(window);
nativeMethods.SendCloseMessageTo(window);
logger.Info($"Sent close message to window '{title}' with handle = {window}.");
}
public bool Hide(IntPtr window)
{
var title = nativeMethods.GetWindowTitle(window);
var success = nativeMethods.HideWindow(window);
if (success)
{
logger.Info($"Hid window '{title}' with handle = {window}.");
}
else
{
logger.Warn($"Failed to hide window '{title}' with handle = {window}!");
}
return success;
}
public void StartMonitoringWindows()
{
captureHookId = nativeMethods.RegisterSystemCaptureStartEvent(OnWindowChanged);
logger.Info($"Registered system capture start event with ID = {captureHookId}.");
foregroundHookId = nativeMethods.RegisterSystemForegroundEvent(OnWindowChanged);
logger.Info($"Registered system foreground event with ID = {foregroundHookId}.");
}
public void StopMonitoringWindows()
{
if (captureHookId.HasValue)
{
nativeMethods.DeregisterSystemEventHook(captureHookId.Value);
logger.Info($"Unregistered system capture start event with ID = {captureHookId}.");
}
if (foregroundHookId.HasValue)
{
nativeMethods.DeregisterSystemEventHook(foregroundHookId.Value);
logger.Info($"Unregistered system foreground event with ID = {foregroundHookId}.");
}
}
private void OnWindowChanged(IntPtr window)
{
if (window != IntPtr.Zero && activeWindow != window)
{
logger.Debug($"Window has changed from {activeWindow} to {window}.");
activeWindow = window;
WindowChanged?.Invoke(window);
}
}
}
}

View file

@ -171,7 +171,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication
public void MustHandleConfigurationRequestCorrectly()
{
var args = default(ClientConfigurationEventArgs);
var configuration = new ClientConfiguration { Settings = new ApplicationSettings { AdminPasswordHash = "12345" } };
var configuration = new ClientConfiguration { Settings = new AppSettings { AdminPasswordHash = "12345" } };
sut.AllowConnection = true;
sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; };

View file

@ -37,7 +37,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
private Mock<IRuntimeHost> runtimeHost;
private SessionConfiguration session;
private SessionContext sessionContext;
private ApplicationSettings settings;
private AppSettings settings;
private ClientOperation sut;
[TestInitialize]
@ -53,7 +53,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
runtimeHost = new Mock<IRuntimeHost>();
session = new SessionConfiguration();
sessionContext = new SessionContext();
settings = new ApplicationSettings();
settings = new AppSettings();
terminated = new Action(() =>
{
runtimeHost.Raise(h => h.ClientDisconnected += null);

View file

@ -56,7 +56,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustUseCommandLineArgumentAs1stPrio()
{
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
@ -77,7 +77,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustUseProgramDataAs2ndPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
appConfig.ProgramDataFilePath = location;
@ -94,7 +94,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustUseAppDataAs3rdPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
appConfig.AppDataFilePath = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
@ -109,7 +109,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustTestdatalyHandleBrowserResource()
{
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
nextSession.Settings = settings;
@ -125,7 +125,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustFallbackToDefaultsAsLastPrio()
{
var defaultSettings = new ApplicationSettings();
var defaultSettings = new AppSettings();
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
@ -141,7 +141,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustAbortIfWishedByUser()
{
var settings = new ApplicationSettings();
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
sessionContext.Current = null;
@ -166,7 +166,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustNotAbortIfNotWishedByUser()
{
var settings = new ApplicationSettings();
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
settings.ConfigurationMode = ConfigurationMode.ConfigureClient;
@ -191,7 +191,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustInformAboutClientConfigurationError()
{
var informed = false;
var settings = new ApplicationSettings();
var settings = new AppSettings();
var url = @"http://www.safeexambrowser.org/whatever.seb";
settings.ConfigurationMode = ConfigurationMode.ConfigureClient;
@ -216,7 +216,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustNotAllowToAbortIfNotInConfigureClientMode()
{
var settings = new ApplicationSettings();
var settings = new AppSettings();
settings.ConfigurationMode = ConfigurationMode.Exam;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
@ -238,7 +238,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustNotFailWithoutCommandLineArgs()
{
var defaultSettings = new ApplicationSettings();
var defaultSettings = new AppSettings();
var result = OperationResult.Failed;
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
@ -272,8 +272,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustOnlyAllowToEnterAdminPasswordFiveTimes()
{
var count = 0;
var localSettings = new ApplicationSettings { AdminPasswordHash = "1234" };
var settings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var localSettings = new AppSettings { AdminPasswordHash = "1234" };
var settings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
@ -302,7 +302,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustOnlyAllowToEnterSettingsPasswordFiveTimes()
{
var count = 0;
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
@ -329,8 +329,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustSucceedIfAdminPasswordTestdata()
{
var password = "test";
var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
nextSession.Settings = nextSettings;
@ -359,8 +359,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustNotAuthenticateIfSameAdminPassword()
{
var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
nextSession.Settings = nextSettings;
@ -388,7 +388,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustSucceedIfSettingsPasswordTestdata()
{
var password = "test";
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
@ -416,7 +416,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
var settings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.Exam };
var settings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.Exam };
appConfig.AppDataFilePath = location;
@ -442,8 +442,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Perform_MustAbortAskingForAdminPasswordIfDecidedByUser()
{
var password = "test";
var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient };
var nextSettings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient };
var url = @"http://www.safeexambrowser.org/whatever.seb";
appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME);
@ -472,7 +472,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Perform_MustAbortAskingForSettingsPasswordIfDecidedByUser()
{
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.PasswordNeeded);
@ -494,10 +494,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Repeat_MustPerformForExamWithTestdataUri()
{
var currentSettings = new ApplicationSettings();
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
@ -515,10 +515,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Repeat_MustPerformForClientConfigurationWithTestdataUri()
{
var currentSettings = new ApplicationSettings();
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;
@ -538,7 +538,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Repeat_MustFailWithInvalidUri()
{
var resource = new Uri("file:///C:/does/not/exist.txt");
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
sessionContext.ReconfigurationFilePath = null;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, It.IsAny<PasswordParameters>())).Returns(LoadStatus.Success);
@ -559,10 +559,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
[TestMethod]
public void Repeat_MustAbortForSettingsPasswordIfWishedByUser()
{
var currentSettings = new ApplicationSettings();
var currentSettings = new AppSettings();
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME));
var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient };
currentSession.Settings = currentSettings;
sessionContext.ReconfigurationFilePath = resource.LocalPath;

View file

@ -21,12 +21,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public class KioskModeOperationTests
{
private SessionConfiguration currentSession;
private ApplicationSettings currentSettings;
private AppSettings currentSettings;
private Mock<IDesktopFactory> desktopFactory;
private Mock<IExplorerShell> explorerShell;
private Mock<ILogger> logger;
private SessionConfiguration nextSession;
private ApplicationSettings nextSettings;
private AppSettings nextSettings;
private Mock<IProcessFactory> processFactory;
private SessionContext sessionContext;
@ -36,12 +36,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
public void Initialize()
{
currentSession = new SessionConfiguration();
currentSettings = new ApplicationSettings();
currentSettings = new AppSettings();
desktopFactory = new Mock<IDesktopFactory>();
explorerShell = new Mock<IExplorerShell>();
logger = new Mock<ILogger>();
nextSession = new SessionConfiguration();
nextSettings = new ApplicationSettings();
nextSettings = new AppSettings();
processFactory = new Mock<IProcessFactory>();
sessionContext = new SessionContext();

View file

@ -34,7 +34,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
private EventWaitHandle serviceEvent;
private SessionConfiguration session;
private SessionContext sessionContext;
private ApplicationSettings settings;
private AppSettings settings;
private Mock<IUserInfo> userInfo;
private ServiceOperation sut;
@ -50,7 +50,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
serviceEvent = new EventWaitHandle(false, EventResetMode.AutoReset, serviceEventName);
session = new SessionConfiguration();
sessionContext = new SessionContext();
settings = new ApplicationSettings();
settings = new AppSettings();
userInfo = new Mock<IUserInfo>();
appConfig.ServiceEventName = serviceEventName;

View file

@ -9,10 +9,10 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Runtime.Operations;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Settings.Logging;
namespace SafeExamBrowser.Runtime.UnitTests.Operations
@ -23,7 +23,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
private SessionConfiguration currentSession;
private Mock<ILogger> logger;
private SessionConfiguration nextSession;
private ApplicationSettings nextSettings;
private AppSettings nextSettings;
private SessionContext sessionContext;
private SessionActivationOperation sut;
@ -34,7 +34,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
currentSession = new SessionConfiguration();
logger = new Mock<ILogger>();
nextSession = new SessionConfiguration();
nextSettings = new ApplicationSettings();
nextSettings = new AppSettings();
sessionContext = new SessionContext();
nextSession.Settings = nextSettings;

View file

@ -36,11 +36,11 @@ namespace SafeExamBrowser.Runtime.UnitTests
private Mock<IProcess> clientProcess;
private Mock<IClientProxy> clientProxy;
private SessionConfiguration currentSession;
private ApplicationSettings currentSettings;
private AppSettings currentSettings;
private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox;
private SessionConfiguration nextSession;
private ApplicationSettings nextSettings;
private AppSettings nextSettings;
private Mock<Action> shutdown;
private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory;
@ -58,11 +58,11 @@ namespace SafeExamBrowser.Runtime.UnitTests
clientProcess = new Mock<IProcess>();
clientProxy = new Mock<IClientProxy>();
currentSession = new SessionConfiguration();
currentSettings = new ApplicationSettings();
currentSettings = new AppSettings();
logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>();
nextSession = new SessionConfiguration();
nextSettings = new ApplicationSettings();
nextSettings = new AppSettings();
runtimeHost = new Mock<IRuntimeHost>();
service = new Mock<IServiceProxy>();
sessionContext = new SessionContext();
@ -134,7 +134,7 @@ namespace SafeExamBrowser.Runtime.UnitTests
var args = new ClientConfigurationEventArgs();
var nextAppConfig = new AppConfig();
var nextSessionId = Guid.NewGuid();
var nextSettings = new ApplicationSettings();
var nextSettings = new AppSettings();
nextSession.AppConfig = nextAppConfig;
nextSession.SessionId = nextSessionId;

View file

@ -107,7 +107,7 @@ namespace SafeExamBrowser.Runtime.Operations
{
var currentPassword = default(string);
var passwordParams = default(PasswordParameters);
var settings = default(ApplicationSettings);
var settings = default(AppSettings);
var status = default(LoadStatus?);
if (source == UriSource.CommandLine)
@ -161,7 +161,7 @@ namespace SafeExamBrowser.Runtime.Operations
}
}
private OperationResult DetermineLoadResult(Uri uri, UriSource source, ApplicationSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string))
private OperationResult DetermineLoadResult(Uri uri, UriSource source, AppSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string))
{
if (status == LoadStatus.LoadWithBrowser || status == LoadStatus.Success)
{
@ -218,7 +218,7 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Failed;
}
private LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out ApplicationSettings settings, string currentPassword = default(string))
private LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out AppSettings settings, string currentPassword = default(string))
{
passwordParams = new PasswordParameters { Password = string.Empty, IsHash = true };

View file

@ -6,11 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Runtime.Operations

View file

@ -21,9 +21,9 @@ namespace SafeExamBrowser.Runtime.Operations
public abstract event ActionRequiredEventHandler ActionRequired;
public abstract event StatusChangedEventHandler StatusChanged;
public SessionOperation(SessionContext sessionContext)
public SessionOperation(SessionContext context)
{
Context = sessionContext;
Context = context;
}
public abstract OperationResult Perform();

View file

@ -27,7 +27,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
private Mock<IFeatureConfigurationFactory> factory;
private Mock<IFeatureConfigurationMonitor> monitor;
private Mock<ILogger> logger;
private ApplicationSettings settings;
private AppSettings settings;
private SessionContext sessionContext;
private LockdownOperation sut;
@ -38,7 +38,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
factory = new Mock<IFeatureConfigurationFactory>();
monitor = new Mock<IFeatureConfigurationMonitor>();
logger = new Mock<ILogger>();
settings = new ApplicationSettings();
settings = new AppSettings();
sessionContext = new SessionContext
{
Configuration = new ServiceConfiguration { Settings = settings, UserName = "TestName", UserSid = "S-1-234-TEST" }

View file

@ -42,7 +42,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
sessionContext.Configuration = new ServiceConfiguration
{
AppConfig = new AppConfig { ServiceEventName = $"{nameof(SafeExamBrowser)}-{nameof(SessionInitializationOperationTests)}" },
Settings = new ApplicationSettings()
Settings = new AppSettings()
};
sut = new SessionInitializationOperation(logger.Object, serviceEventFactory.Object, sessionContext);

View file

@ -82,7 +82,7 @@ namespace SafeExamBrowser.Service.UnitTests
{
AppConfig = new AppConfig { ServiceLogFilePath = "Test.log" },
SessionId = Guid.NewGuid(),
Settings = new ApplicationSettings { LogLevel = LogLevel.Warning }
Settings = new AppSettings { LogLevel = LogLevel.Warning }
}
};

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Settings.Applications;
using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.Monitoring;
@ -20,7 +21,7 @@ namespace SafeExamBrowser.Settings
/// Defines all settings for the application.
/// </summary>
[Serializable]
public class ApplicationSettings
public class AppSettings
{
/// <summary>
/// All action center-related settings.
@ -37,6 +38,11 @@ namespace SafeExamBrowser.Settings
/// </summary>
public bool AllowApplicationLogAccess { get; set; }
/// <summary>
/// All settings related to third-party applications.
/// </summary>
public ApplicationSettings Applications { get; set; }
/// <summary>
/// All audio-related settings.
/// </summary>
@ -92,9 +98,10 @@ namespace SafeExamBrowser.Settings
/// </summary>
public UserInterfaceMode UserInterfaceMode { get; set; }
public ApplicationSettings()
public AppSettings()
{
ActionCenter = new ActionCenterSettings();
Applications = new ApplicationSettings();
Audio = new AudioSettings();
Browser = new BrowserSettings();
Keyboard = new KeyboardSettings();

View file

@ -0,0 +1,36 @@
/*
* Copyright (c) 2019 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.Collections.Generic;
namespace SafeExamBrowser.Settings.Applications
{
/// <summary>
/// TODO
/// </summary>
[Serializable]
public class ApplicationSettings
{
/// <summary>
///
/// </summary>
public IList<BlacklistApplication> Blacklist { get; set; }
/// <summary>
///
/// </summary>
public IList<WhitelistApplication> Whitelist { get; set; }
public ApplicationSettings()
{
Blacklist = new List<BlacklistApplication>();
Whitelist = new List<WhitelistApplication>();
}
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (c) 2019 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;
namespace SafeExamBrowser.Settings.Applications
{
/// <summary>
/// TODO
/// </summary>
[Serializable]
public class BlacklistApplication
{
/// <summary>
///
/// </summary>
public bool AutoTerminate { get; set; }
/// <summary>
///
/// </summary>
public string ExecutableName { get; set; }
/// <summary>
///
/// </summary>
public string ExecutableOriginalName { get; set; }
}
}

View file

@ -8,10 +8,13 @@
using System;
namespace SafeExamBrowser.Monitoring.Contracts.Windows.Events
namespace SafeExamBrowser.Settings.Applications
{
/// <summary>
/// Indicates that the input focus has changed to the window with the specified handle.
/// TODO
/// </summary>
public delegate void WindowChangedEventHandler(IntPtr window);
[Serializable]
public class WhitelistApplication
{
}
}

View file

@ -53,6 +53,9 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Applications\ApplicationSettings.cs" />
<Compile Include="Applications\BlacklistApplication.cs" />
<Compile Include="Applications\WhitelistApplication.cs" />
<Compile Include="Browser\BrowserFilterSettings.cs" />
<Compile Include="Browser\BrowserSettings.cs" />
<Compile Include="Browser\BrowserWindowSettings.cs" />
@ -67,11 +70,12 @@
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Service\ServicePolicy.cs" />
<Compile Include="Service\ServiceSettings.cs" />
<Compile Include="ApplicationSettings.cs" />
<Compile Include="AppSettings.cs" />
<Compile Include="SystemComponents\AudioSettings.cs" />
<Compile Include="UserInterface\ActionCenterSettings.cs" />
<Compile Include="UserInterface\TaskbarSettings.cs" />
<Compile Include="UserInterface\UserInterfaceMode.cs" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -8,10 +8,10 @@
using System;
namespace SafeExamBrowser.Monitoring.Contracts.Keyboard
namespace SafeExamBrowser.WindowsApi.Contracts.Events
{
/// <summary>
/// The key modifiers which can be detected by the <see cref="IKeyboardInterceptor"/>.
/// The key modifiers which can be detected by a keyboard hook.
/// </summary>
[Flags]
public enum KeyModifier

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Monitoring.Contracts.Keyboard
namespace SafeExamBrowser.WindowsApi.Contracts.Events
{
/// <summary>
/// The key states which can be detected by the <see cref="IKeyboardInterceptor"/>.
/// The key states which can be detected by a keyboard hook.
/// </summary>
public enum KeyState
{

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2019 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.Events
{
/// <summary>
/// The callback for a keyboard hook. Return <c>true</c> to consume (i.e. block) the user input, otherwise <c>false</c>.
/// </summary>
public delegate bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state);
}

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Monitoring.Contracts.Mouse
namespace SafeExamBrowser.WindowsApi.Contracts.Events
{
/// <summary>
/// The mouse buttons which can be detected by the <see cref="IMouseInterceptor"/>.
/// The mouse buttons which can be detected by a mouse hook.
/// </summary>
public enum MouseButton
{

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Monitoring.Contracts.Mouse
namespace SafeExamBrowser.WindowsApi.Contracts.Events
{
/// <summary>
/// The mouse button states which can be detected by the <see cref="IMouseInterceptor"/>.
/// The mouse button states which can be detected a mouse hook.
/// </summary>
public enum MouseButtonState
{

View file

@ -0,0 +1,15 @@
/*
* Copyright (c) 2019 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.Events
{
/// <summary>
/// The callback for a mouse hook. Return <c>true</c> to consume (i.e. block) the user input, otherwise <c>false</c>.
/// </summary>
public delegate bool MouseHookCallback(MouseButton button, MouseButtonState state);
}

View file

@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.WindowsApi.Contracts
{
/// <summary>

View file

@ -8,8 +8,6 @@
using System;
using System.Collections.Generic;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.WindowsApi.Contracts
@ -20,20 +18,20 @@ namespace SafeExamBrowser.WindowsApi.Contracts
public interface INativeMethods
{
/// <summary>
/// Deregisters the system hook for the given keyboard interceptor.
/// Deregisters a previously registered keyboard hook.
/// </summary>
/// <exception cref="System.ComponentModel.Win32Exception">
/// If the hook for the given interceptor could not be successfully removed.
/// If the hook could not be successfully removed.
/// </exception>
void DeregisterKeyboardHook(IKeyboardInterceptor interceptor);
void DeregisterKeyboardHook(Guid hookId);
/// <summary>
/// Deregisters the system hook for the given mouse interceptor.
/// Deregisters a previously registered mouse hook.
/// </summary>
/// <exception cref="System.ComponentModel.Win32Exception">
/// If the hook for the given interceptor could not be successfully removed.
/// If the hook could not be successfully removed.
/// </exception>
void DeregisterMouseHook(IMouseInterceptor interceptor);
void DeregisterMouseHook(Guid hookId);
/// <summary>
/// Deregisters a previously registered system event hook.
@ -65,14 +63,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts
uint GetProcessIdFor(IntPtr window);
/// <summary>
/// Retrieves a window handle to the Windows taskbar. Returns <c>IntPtr.Zero</c>
/// if the taskbar could not be found (i.e. if it isn't running).
/// Retrieves a window handle to the Windows taskbar. Returns <c>IntPtr.Zero</c> if the taskbar could not be found (i.e. if it isn't running).
/// </summary>
IntPtr GetShellWindowHandle();
/// <summary>
/// Retrieves the process ID of the main Windows explorer instance controlling
/// desktop and taskbar or <c>0</c>, if the process isn't running.
/// Retrieves the process identifier of the main Windows explorer instance controlling desktop and taskbar or <c>0</c>, if the process isn't running.
/// </summary>
uint GetShellProcessId();
@ -121,24 +117,24 @@ namespace SafeExamBrowser.WindowsApi.Contracts
void PreventSleepMode();
/// <summary>
/// Registers a system hook for the given keyboard interceptor.
/// Registers a keyboard hook for the given callback. Returns the identifier of the newly registered hook.
/// </summary>
void RegisterKeyboardHook(IKeyboardInterceptor interceptor);
Guid RegisterKeyboardHook(KeyboardHookCallback callback);
/// <summary>
/// Registers a system hook for the given mouse interceptor.
/// Registers a mouse hook for the given callback. Returns the identifier of the newly registered hook.
/// </summary>
void RegisterMouseHook(IMouseInterceptor interceptor);
Guid RegisterMouseHook(MouseHookCallback callback);
/// <summary>
/// Registers a system event which will invoke the specified callback when a window has received mouse capture.
/// Returns the ID of the newly registered Windows event hook.
/// Registers a system event which will invoke the specified callback when a window has received mouse capture. Returns the identifier of
/// the newly registered Windows event hook.
/// </summary>
Guid RegisterSystemCaptureStartEvent(SystemEventCallback callback);
/// <summary>
/// Registers a system event which will invoke the specified callback when the foreground window has changed.
/// Returns a handle to the newly registered Windows event hook.
/// Registers a system event which will invoke the specified callback when the foreground window has changed. Returns the identifier of the
/// newly registered Windows event hook.
/// </summary>
Guid RegisterSystemForegroundEvent(SystemEventCallback callback);
@ -156,7 +152,7 @@ namespace SafeExamBrowser.WindowsApi.Contracts
void RestoreWindow(IntPtr window);
/// <summary>
/// Attempts to resume the thread referenced by the given thread ID. Returns <c>true</c> if the thread was successfully resumed,
/// Attempts to resume the thread referenced by the given thread identifier. Returns <c>true</c> if the thread was successfully resumed,
/// otherwise <c>false</c>.
/// </summary>
bool ResumeThread(int threadId);
@ -183,7 +179,7 @@ namespace SafeExamBrowser.WindowsApi.Contracts
void SetWorkingArea(IBounds bounds);
/// <summary>
/// Attempts to suspend the thread referenced by the given thread ID. Returns <c>true</c> if the thread was successfully suspended,
/// Attempts to suspend the thread referenced by the given thread identifier. Returns <c>true</c> if the thread was successfully suspended,
/// otherwise <c>false</c>.
/// </summary>
bool SuspendThread(int threadId);

View file

@ -53,6 +53,12 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Events\KeyboardHookCallback.cs" />
<Compile Include="Events\KeyModifier.cs" />
<Compile Include="Events\KeyState.cs" />
<Compile Include="Events\MouseButton.cs" />
<Compile Include="Events\MouseButtonState.cs" />
<Compile Include="Events\MouseHookCallback.cs" />
<Compile Include="Events\ProcessTerminatedEventHandler.cs" />
<Compile Include="Events\SystemEventCallback.cs" />
<Compile Include="Events\TerminationActivatorEventHandler.cs" />
@ -66,11 +72,5 @@
<Compile Include="ITerminationActivator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Monitoring.Contracts\SafeExamBrowser.Monitoring.Contracts.csproj">
<Project>{6d563a30-366d-4c35-815b-2c9e6872278b}</Project>
<Name>SafeExamBrowser.Monitoring.Contracts</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>

View file

@ -8,8 +8,8 @@
using System;
using System.Runtime.InteropServices;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types;
@ -18,14 +18,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks
internal class KeyboardHook
{
private bool altPressed, ctrlPressed;
private KeyboardHookCallback callback;
private IntPtr handle;
private HookDelegate hookDelegate;
internal IntPtr Handle { get; private set; }
internal IKeyboardInterceptor Interceptor { get; private set; }
internal Guid Id { get; private set; }
internal KeyboardHook(IKeyboardInterceptor interceptor)
internal KeyboardHook(KeyboardHookCallback callback)
{
Interceptor = interceptor;
this.callback = callback;
this.Id = Guid.NewGuid();
}
internal void Attach()
@ -38,13 +40,12 @@ namespace SafeExamBrowser.WindowsApi.Hooks
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc);
Handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0);
handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0);
}
internal bool Detach()
{
return User32.UnhookWindowsHookEx(Handle);
return User32.UnhookWindowsHookEx(handle);
}
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
@ -55,13 +56,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks
var modifier = GetModifiers(keyData, wParam.ToInt32());
var state = GetState(wParam.ToInt32());
if (Interceptor.Block((int) keyData.KeyCode, modifier, state))
if (callback((int) keyData.KeyCode, modifier, state))
{
return (IntPtr) 1;
}
}
return User32.CallNextHookEx(Handle, nCode, wParam, lParam);
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
}
private KeyState GetState(int wParam)

View file

@ -8,8 +8,8 @@
using System;
using System.Runtime.InteropServices;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types;
@ -17,14 +17,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks
{
internal class MouseHook
{
private MouseHookCallback callback;
private IntPtr handle;
private HookDelegate hookDelegate;
internal IntPtr Handle { get; private set; }
internal IMouseInterceptor Interceptor { get; private set; }
internal Guid Id { get; private set; }
internal MouseHook(IMouseInterceptor interceptor)
internal MouseHook(MouseHookCallback callback)
{
Interceptor = interceptor;
this.callback = callback;
this.Id = Guid.NewGuid();
}
internal void Attach()
@ -37,13 +39,12 @@ namespace SafeExamBrowser.WindowsApi.Hooks
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelMouseProc);
Handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0);
handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0);
}
internal bool Detach()
{
return User32.UnhookWindowsHookEx(Handle);
return User32.UnhookWindowsHookEx(handle);
}
private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
@ -54,13 +55,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks
var button = GetButton(wParam.ToInt32());
var state = GetState(wParam.ToInt32());
if (Interceptor.Block(button, state))
if (callback(button, state))
{
return (IntPtr) 1;
}
}
return User32.CallNextHookEx(Handle, nCode, wParam, lParam);
return User32.CallNextHookEx(handle, nCode, wParam, lParam);
}
private bool Ignore(int wParam)

View file

@ -8,8 +8,8 @@
using System;
using System.Threading;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates;
namespace SafeExamBrowser.WindowsApi.Hooks
@ -21,8 +21,8 @@ namespace SafeExamBrowser.WindowsApi.Hooks
private bool detachSuccess;
private EventDelegate eventDelegate;
private uint eventId;
private IntPtr handle;
internal IntPtr Handle { get; private set; }
internal Guid Id { get; private set; }
public SystemHook(SystemEventCallback callback, uint eventId)
@ -40,14 +40,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks
// Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code.
// Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
eventDelegate = new EventDelegate(LowLevelSystemProc);
Handle = User32.SetWinEventHook(eventId, eventId, IntPtr.Zero, eventDelegate, 0, 0, Constant.WINEVENT_OUTOFCONTEXT);
handle = User32.SetWinEventHook(eventId, eventId, IntPtr.Zero, eventDelegate, 0, 0, Constant.WINEVENT_OUTOFCONTEXT);
}
internal void AwaitDetach()
{
detachEvent.WaitOne();
detachSuccess = User32.UnhookWinEvent(Handle);
detachSuccess = User32.UnhookWinEvent(handle);
detachResultAvailableEvent.Set();
}

View file

@ -14,8 +14,6 @@ using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events;
@ -26,34 +24,34 @@ namespace SafeExamBrowser.WindowsApi
{
public class NativeMethods : INativeMethods
{
private ConcurrentDictionary<IntPtr, KeyboardHook> KeyboardHooks = new ConcurrentDictionary<IntPtr, KeyboardHook>();
private ConcurrentDictionary<IntPtr, MouseHook> MouseHooks = new ConcurrentDictionary<IntPtr, MouseHook>();
private ConcurrentDictionary<IntPtr, SystemHook> SystemHooks = new ConcurrentDictionary<IntPtr, SystemHook>();
private ConcurrentBag<KeyboardHook> KeyboardHooks = new ConcurrentBag<KeyboardHook>();
private ConcurrentBag<MouseHook> MouseHooks = new ConcurrentBag<MouseHook>();
private ConcurrentBag<SystemHook> SystemHooks = new ConcurrentBag<SystemHook>();
/// <summary>
/// Upon finalization, unregister all active system events and hooks...
/// </summary>
~NativeMethods()
{
foreach (var hook in SystemHooks.Values)
foreach (var hook in SystemHooks)
{
hook.Detach();
}
foreach (var hook in KeyboardHooks.Values)
foreach (var hook in KeyboardHooks)
{
hook.Detach();
}
foreach (var hook in MouseHooks.Values)
foreach (var hook in MouseHooks)
{
hook.Detach();
}
}
public void DeregisterKeyboardHook(IKeyboardInterceptor interceptor)
public void DeregisterKeyboardHook(Guid hookId)
{
var hook = KeyboardHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor);
var hook = KeyboardHooks.FirstOrDefault(h => h.Id == hookId);
if (hook != null)
{
@ -64,13 +62,13 @@ namespace SafeExamBrowser.WindowsApi
throw new Win32Exception(Marshal.GetLastWin32Error());
}
KeyboardHooks.TryRemove(hook.Handle, out _);
KeyboardHooks.TryTake(out _);
}
}
public void DeregisterMouseHook(IMouseInterceptor interceptor)
public void DeregisterMouseHook(Guid hookId)
{
var hook = MouseHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor);
var hook = MouseHooks.FirstOrDefault(h => h.Id == hookId);
if (hook != null)
{
@ -81,13 +79,13 @@ namespace SafeExamBrowser.WindowsApi
throw new Win32Exception(Marshal.GetLastWin32Error());
}
MouseHooks.TryRemove(hook.Handle, out _);
MouseHooks.TryTake(out _);
}
}
public void DeregisterSystemEventHook(Guid hookId)
{
var hook = SystemHooks.Values.FirstOrDefault(h => h.Id == hookId);
var hook = SystemHooks.FirstOrDefault(h => h.Id == hookId);
if (hook != null)
{
@ -98,7 +96,7 @@ namespace SafeExamBrowser.WindowsApi
throw new Win32Exception(Marshal.GetLastWin32Error());
}
SystemHooks.TryRemove(hook.Handle, out _);
SystemHooks.TryTake(out _);
}
}
@ -236,16 +234,18 @@ namespace SafeExamBrowser.WindowsApi
Kernel32.SetThreadExecutionState(EXECUTION_STATE.CONTINUOUS | EXECUTION_STATE.DISPLAY_REQUIRED | EXECUTION_STATE.SYSTEM_REQUIRED);
}
public void RegisterKeyboardHook(IKeyboardInterceptor interceptor)
public Guid RegisterKeyboardHook(KeyboardHookCallback callback)
{
var hookId = default(Guid);
var hookReadyEvent = new AutoResetEvent(false);
var hookThread = new Thread(() =>
{
var hook = new KeyboardHook(interceptor);
var hook = new KeyboardHook(callback);
var sleepEvent = new AutoResetEvent(false);
hook.Attach();
KeyboardHooks[hook.Handle] = hook;
hookId = hook.Id;
KeyboardHooks.Add(hook);
hookReadyEvent.Set();
while (true)
@ -259,18 +259,22 @@ namespace SafeExamBrowser.WindowsApi
hookThread.Start();
hookReadyEvent.WaitOne();
return hookId;
}
public void RegisterMouseHook(IMouseInterceptor interceptor)
public Guid RegisterMouseHook(MouseHookCallback callback)
{
var hookId = default(Guid);
var hookReadyEvent = new AutoResetEvent(false);
var hookThread = new Thread(() =>
{
var hook = new MouseHook(interceptor);
var hook = new MouseHook(callback);
var sleepEvent = new AutoResetEvent(false);
hook.Attach();
MouseHooks[hook.Handle] = hook;
hookId = hook.Id;
MouseHooks.Add(hook);
hookReadyEvent.Set();
while (true)
@ -284,6 +288,8 @@ namespace SafeExamBrowser.WindowsApi
hookThread.Start();
hookReadyEvent.WaitOne();
return hookId;
}
public Guid RegisterSystemCaptureStartEvent(SystemEventCallback callback)
@ -306,7 +312,7 @@ namespace SafeExamBrowser.WindowsApi
hook.Attach();
hookId = hook.Id;
SystemHooks[hook.Handle] = hook;
SystemHooks.Add(hook);
hookReadyEvent.Set();
hook.AwaitDetach();
});

View file

@ -96,10 +96,6 @@
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.Monitoring.Contracts\SafeExamBrowser.Monitoring.Contracts.csproj">
<Project>{6d563a30-366d-4c35-815b-2c9e6872278b}</Project>
<Name>SafeExamBrowser.Monitoring.Contracts</Name>
</ProjectReference>
<ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
<Name>SafeExamBrowser.UserInterface.Contracts</Name>