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
SafeExamBrowser.Client.Contracts
SafeExamBrowser.Client.UnitTests
SafeExamBrowser.Client
SafeExamBrowser.Configuration.Contracts
SafeExamBrowser.Configuration
SafeExamBrowser.Monitoring.Contracts
SafeExamBrowser.Monitoring
SafeExamBrowser.Runtime.UnitTests
SafeExamBrowser.Runtime/Operations
SafeExamBrowser.Service.UnitTests
SafeExamBrowser.Settings
SafeExamBrowser.WindowsApi.Contracts
SafeExamBrowser.WindowsApi

View file

@ -10,6 +10,7 @@ using System;
using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Browser.Contracts;
using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Communication.Contracts.Hosts;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Client.Contracts namespace SafeExamBrowser.Client.Contracts
{ {
@ -41,7 +42,7 @@ namespace SafeExamBrowser.Client.Contracts
/// <summary> /// <summary>
/// The settings to be used during application execution. /// The settings to be used during application execution.
/// </summary> /// </summary>
Settings.ApplicationSettings Settings { set; } AppSettings Settings { set; }
/// <summary> /// <summary>
/// Reverts any changes, releases all used resources and terminates the client. /// 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.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display; using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.Processes; using SafeExamBrowser.Settings;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -38,6 +37,7 @@ namespace SafeExamBrowser.Client.UnitTests
{ {
private AppConfig appConfig; private AppConfig appConfig;
private Mock<IActionCenter> actionCenter; private Mock<IActionCenter> actionCenter;
private Mock<IApplicationMonitor> applicationMonitor;
private Mock<IBrowserApplication> browserController; private Mock<IBrowserApplication> browserController;
private Mock<IClientHost> clientHost; private Mock<IClientHost> clientHost;
private Mock<IDisplayMonitor> displayMonitor; private Mock<IDisplayMonitor> displayMonitor;
@ -45,17 +45,15 @@ namespace SafeExamBrowser.Client.UnitTests
private Mock<IHashAlgorithm> hashAlgorithm; private Mock<IHashAlgorithm> hashAlgorithm;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<IMessageBox> messageBox; private Mock<IMessageBox> messageBox;
private Mock<IProcessMonitor> processMonitor;
private Mock<IOperationSequence> operationSequence; private Mock<IOperationSequence> operationSequence;
private Mock<IRuntimeProxy> runtimeProxy; private Mock<IRuntimeProxy> runtimeProxy;
private Guid sessionId; private Guid sessionId;
private ApplicationSettings settings; private AppSettings settings;
private Mock<Action> shutdown; private Mock<Action> shutdown;
private Mock<ITaskbar> taskbar; private Mock<ITaskbar> taskbar;
private Mock<ITerminationActivator> terminationActivator; private Mock<ITerminationActivator> terminationActivator;
private Mock<IText> text; private Mock<IText> text;
private Mock<IUserInterfaceFactory> uiFactory; private Mock<IUserInterfaceFactory> uiFactory;
private Mock<IWindowMonitor> windowMonitor;
private ClientController sut; private ClientController sut;
@ -64,6 +62,7 @@ namespace SafeExamBrowser.Client.UnitTests
{ {
appConfig = new AppConfig(); appConfig = new AppConfig();
actionCenter = new Mock<IActionCenter>(); actionCenter = new Mock<IActionCenter>();
applicationMonitor = new Mock<IApplicationMonitor>();
browserController = new Mock<IBrowserApplication>(); browserController = new Mock<IBrowserApplication>();
clientHost = new Mock<IClientHost>(); clientHost = new Mock<IClientHost>();
displayMonitor = new Mock<IDisplayMonitor>(); displayMonitor = new Mock<IDisplayMonitor>();
@ -71,17 +70,15 @@ namespace SafeExamBrowser.Client.UnitTests
hashAlgorithm = new Mock<IHashAlgorithm>(); hashAlgorithm = new Mock<IHashAlgorithm>();
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
messageBox = new Mock<IMessageBox>(); messageBox = new Mock<IMessageBox>();
processMonitor = new Mock<IProcessMonitor>();
operationSequence = new Mock<IOperationSequence>(); operationSequence = new Mock<IOperationSequence>();
runtimeProxy = new Mock<IRuntimeProxy>(); runtimeProxy = new Mock<IRuntimeProxy>();
sessionId = Guid.NewGuid(); sessionId = Guid.NewGuid();
settings = new ApplicationSettings(); settings = new AppSettings();
shutdown = new Mock<Action>(); shutdown = new Mock<Action>();
taskbar = new Mock<ITaskbar>(); taskbar = new Mock<ITaskbar>();
terminationActivator = new Mock<ITerminationActivator>(); terminationActivator = new Mock<ITerminationActivator>();
text = new Mock<IText>(); text = new Mock<IText>();
uiFactory = new Mock<IUserInterfaceFactory>(); uiFactory = new Mock<IUserInterfaceFactory>();
windowMonitor = new Mock<IWindowMonitor>();
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success); operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true)); runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true));
@ -89,20 +86,19 @@ namespace SafeExamBrowser.Client.UnitTests
sut = new ClientController( sut = new ClientController(
actionCenter.Object, actionCenter.Object,
applicationMonitor.Object,
displayMonitor.Object, displayMonitor.Object,
explorerShell.Object, explorerShell.Object,
hashAlgorithm.Object, hashAlgorithm.Object,
logger.Object, logger.Object,
messageBox.Object, messageBox.Object,
operationSequence.Object, operationSequence.Object,
processMonitor.Object,
runtimeProxy.Object, runtimeProxy.Object,
shutdown.Object, shutdown.Object,
taskbar.Object, taskbar.Object,
terminationActivator.Object, terminationActivator.Object,
text.Object, text.Object,
uiFactory.Object, uiFactory.Object);
windowMonitor.Object);
sut.AppConfig = appConfig; sut.AppConfig = appConfig;
sut.Browser = browserController.Object; sut.Browser = browserController.Object;
@ -111,6 +107,30 @@ namespace SafeExamBrowser.Client.UnitTests
sut.Settings = settings; 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] [TestMethod]
public void Communication_MustCorrectlyHandleMessageBoxRequest() 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); 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] [TestMethod]
public void Reconfiguration_MustDenyIfInExamMode() public void Reconfiguration_MustDenyIfInExamMode()
{ {
@ -642,67 +638,5 @@ namespace SafeExamBrowser.Client.UnitTests
terminationActivator.Verify(t => t.Resume(), Times.Once); terminationActivator.Verify(t => t.Resume(), Times.Once);
runtimeProxy.Verify(p => p.RequestShutdown(), 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.Data;
using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@ -23,6 +23,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public class ConfigurationOperationTests public class ConfigurationOperationTests
{ {
private ClientConfiguration configuration; private ClientConfiguration configuration;
private ClientContext context;
private Mock<ILogger> logger; private Mock<ILogger> logger;
private Mock<IRuntimeProxy> runtime; private Mock<IRuntimeProxy> runtime;
private ConfigurationOperation sut; private ConfigurationOperation sut;
@ -31,10 +32,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
public void Initialize() public void Initialize()
{ {
configuration = new ClientConfiguration(); configuration = new ClientConfiguration();
context = new ClientContext();
logger = new Mock<ILogger>(); logger = new Mock<ILogger>();
runtime = new Mock<IRuntimeProxy>(); runtime = new Mock<IRuntimeProxy>();
sut = new ConfigurationOperation(configuration, logger.Object, runtime.Object); sut = new ConfigurationOperation(configuration, context, logger.Object, runtime.Object);
} }
[TestMethod] [TestMethod]
@ -46,7 +48,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
AppConfig = new AppConfig(), AppConfig = new AppConfig(),
SessionId = Guid.NewGuid(), SessionId = Guid.NewGuid(),
Settings = new ApplicationSettings() Settings = new AppSettings()
} }
}; };

View file

@ -11,7 +11,6 @@ using Moq;
using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Client.Operations;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard; using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
private Mock<IKeyboardInterceptor> keyboardInterceptorMock; private Mock<IKeyboardInterceptor> keyboardInterceptorMock;
private Mock<ILogger> loggerMock; private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private KeyboardInterceptorOperation sut; private KeyboardInterceptorOperation sut;
@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
keyboardInterceptorMock = new Mock<IKeyboardInterceptor>(); keyboardInterceptorMock = new Mock<IKeyboardInterceptor>();
loggerMock = new Mock<ILogger>(); 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] [TestMethod]
@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
sut.Perform(); 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] [TestMethod]
@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
sut.Revert(); 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.Client.Operations;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.UnitTests.Operations namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
private Mock<IMouseInterceptor> mouseInterceptorMock; private Mock<IMouseInterceptor> mouseInterceptorMock;
private Mock<ILogger> loggerMock; private Mock<ILogger> loggerMock;
private Mock<INativeMethods> nativeMethodsMock;
private MouseInterceptorOperation sut; private MouseInterceptorOperation sut;
@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
mouseInterceptorMock = new Mock<IMouseInterceptor>(); mouseInterceptorMock = new Mock<IMouseInterceptor>();
loggerMock = new Mock<ILogger>(); 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] [TestMethod]
@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
sut.Perform(); 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] [TestMethod]
@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
{ {
sut.Revert(); 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> </Reference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Operations\ApplicationOperationTests.cs" />
<Compile Include="Operations\BrowserOperationTests.cs" /> <Compile Include="Operations\BrowserOperationTests.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperationTests.cs" /> <Compile Include="Operations\ClientHostDisconnectionOperationTests.cs" />
<Compile Include="Operations\ClipboardOperationTests.cs" /> <Compile Include="Operations\ClipboardOperationTests.cs" />
@ -88,10 +89,8 @@
<Compile Include="Operations\DisplayMonitorOperationTests.cs" /> <Compile Include="Operations\DisplayMonitorOperationTests.cs" />
<Compile Include="Operations\KeyboardInterceptorOperationTests.cs" /> <Compile Include="Operations\KeyboardInterceptorOperationTests.cs" />
<Compile Include="Operations\MouseInterceptorOperationTests.cs" /> <Compile Include="Operations\MouseInterceptorOperationTests.cs" />
<Compile Include="Operations\ProcessMonitorOperationTests.cs" />
<Compile Include="Operations\RuntimeConnectionOperationTests.cs" /> <Compile Include="Operations\RuntimeConnectionOperationTests.cs" />
<Compile Include="Operations\ShellOperationTests.cs" /> <Compile Include="Operations\ShellOperationTests.cs" />
<Compile Include="Operations\WindowMonitorOperationTests.cs" />
<Compile Include="Communication\ClientHostTests.cs" /> <Compile Include="Communication\ClientHostTests.cs" />
<Compile Include="Notifications\AboutNotificationControllerTests.cs" /> <Compile Include="Notifications\AboutNotificationControllerTests.cs" />
<Compile Include="Notifications\LogNotificationControllerTests.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.Communication.Contracts.Proxies;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.Cryptography;
using SafeExamBrowser.Settings;
using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Contracts.Display; using SafeExamBrowser.Monitoring.Contracts.Display;
using SafeExamBrowser.Monitoring.Contracts.Processes; using SafeExamBrowser.Settings;
using SafeExamBrowser.Monitoring.Contracts.Windows;
using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell; using SafeExamBrowser.UserInterface.Contracts.Shell;
@ -36,13 +35,13 @@ namespace SafeExamBrowser.Client
internal class ClientController : IClientController internal class ClientController : IClientController
{ {
private IActionCenter actionCenter; private IActionCenter actionCenter;
private IApplicationMonitor applicationMonitor;
private IDisplayMonitor displayMonitor; private IDisplayMonitor displayMonitor;
private IExplorerShell explorerShell; private IExplorerShell explorerShell;
private IHashAlgorithm hashAlgorithm; private IHashAlgorithm hashAlgorithm;
private ILogger logger; private ILogger logger;
private IMessageBox messageBox; private IMessageBox messageBox;
private IOperationSequence operations; private IOperationSequence operations;
private IProcessMonitor processMonitor;
private IRuntimeProxy runtime; private IRuntimeProxy runtime;
private Action shutdown; private Action shutdown;
private ISplashScreen splashScreen; private ISplashScreen splashScreen;
@ -50,13 +49,12 @@ namespace SafeExamBrowser.Client
private ITerminationActivator terminationActivator; private ITerminationActivator terminationActivator;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
private AppConfig appConfig; private AppConfig appConfig;
public IBrowserApplication Browser { private get; set; } public IBrowserApplication Browser { private get; set; }
public IClientHost ClientHost { private get; set; } public IClientHost ClientHost { private get; set; }
public Guid SessionId { private get; set; } public Guid SessionId { private get; set; }
public ApplicationSettings Settings { private get; set; } public AppSettings Settings { private get; set; }
public AppConfig AppConfig public AppConfig AppConfig
{ {
@ -73,36 +71,34 @@ namespace SafeExamBrowser.Client
public ClientController( public ClientController(
IActionCenter actionCenter, IActionCenter actionCenter,
IApplicationMonitor applicationMonitor,
IDisplayMonitor displayMonitor, IDisplayMonitor displayMonitor,
IExplorerShell explorerShell, IExplorerShell explorerShell,
IHashAlgorithm hashAlgorithm, IHashAlgorithm hashAlgorithm,
ILogger logger, ILogger logger,
IMessageBox messageBox, IMessageBox messageBox,
IOperationSequence operations, IOperationSequence operations,
IProcessMonitor processMonitor,
IRuntimeProxy runtime, IRuntimeProxy runtime,
Action shutdown, Action shutdown,
ITaskbar taskbar, ITaskbar taskbar,
ITerminationActivator terminationActivator, ITerminationActivator terminationActivator,
IText text, IText text,
IUserInterfaceFactory uiFactory, IUserInterfaceFactory uiFactory)
IWindowMonitor windowMonitor)
{ {
this.actionCenter = actionCenter; this.actionCenter = actionCenter;
this.applicationMonitor = applicationMonitor;
this.displayMonitor = displayMonitor; this.displayMonitor = displayMonitor;
this.explorerShell = explorerShell; this.explorerShell = explorerShell;
this.hashAlgorithm = hashAlgorithm; this.hashAlgorithm = hashAlgorithm;
this.logger = logger; this.logger = logger;
this.messageBox = messageBox; this.messageBox = messageBox;
this.operations = operations; this.operations = operations;
this.processMonitor = processMonitor;
this.runtime = runtime; this.runtime = runtime;
this.shutdown = shutdown; this.shutdown = shutdown;
this.taskbar = taskbar; this.taskbar = taskbar;
this.terminationActivator = terminationActivator; this.terminationActivator = terminationActivator;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
this.windowMonitor = windowMonitor;
} }
public bool TryStart() public bool TryStart()
@ -110,6 +106,7 @@ namespace SafeExamBrowser.Client
logger.Info("Initiating startup procedure..."); logger.Info("Initiating startup procedure...");
splashScreen = uiFactory.CreateSplashScreen(); splashScreen = uiFactory.CreateSplashScreen();
operations.ActionRequired += Operations_ActionRequired;
operations.ProgressChanged += Operations_ProgressChanged; operations.ProgressChanged += Operations_ProgressChanged;
operations.StatusChanged += Operations_StatusChanged; operations.StatusChanged += Operations_StatusChanged;
@ -175,28 +172,26 @@ namespace SafeExamBrowser.Client
private void RegisterEvents() private void RegisterEvents()
{ {
actionCenter.QuitButtonClicked += Shell_QuitButtonClicked; actionCenter.QuitButtonClicked += Shell_QuitButtonClicked;
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested; Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested; ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested += ClientHost_PasswordRequested; ClientHost.PasswordRequested += ClientHost_PasswordRequested;
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied; ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
ClientHost.Shutdown += ClientHost_Shutdown; ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost += Runtime_ConnectionLost; runtime.ConnectionLost += Runtime_ConnectionLost;
taskbar.QuitButtonClicked += Shell_QuitButtonClicked; taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
terminationActivator.Activated += TerminationActivator_Activated; terminationActivator.Activated += TerminationActivator_Activated;
windowMonitor.WindowChanged += WindowMonitor_WindowChanged;
} }
private void DeregisterEvents() private void DeregisterEvents()
{ {
actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked; actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked;
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
runtime.ConnectionLost -= Runtime_ConnectionLost; runtime.ConnectionLost -= Runtime_ConnectionLost;
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked; taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
terminationActivator.Activated -= TerminationActivator_Activated; terminationActivator.Activated -= TerminationActivator_Activated;
windowMonitor.WindowChanged -= WindowMonitor_WindowChanged;
if (Browser != null) if (Browser != null)
{ {
@ -226,6 +221,18 @@ namespace SafeExamBrowser.Client
Browser.Start(); 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) private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
{ {
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient) if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
@ -339,6 +346,11 @@ namespace SafeExamBrowser.Client
logger.Info("Desktop successfully restored."); logger.Info("Desktop successfully restored.");
} }
private void Operations_ActionRequired(ActionRequiredEventArgs args)
{
// TODO
}
private void Operations_ProgressChanged(ProgressChangedEventArgs args) private void Operations_ProgressChanged(ProgressChangedEventArgs args)
{ {
if (args.CurrentValue.HasValue) if (args.CurrentValue.HasValue)
@ -372,18 +384,6 @@ namespace SafeExamBrowser.Client
splashScreen?.UpdateStatus(status, true); 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() private void Runtime_ConnectionLost()
{ {
logger.Error("Lost connection to the runtime!"); logger.Error("Lost connection to the runtime!");
@ -406,21 +406,6 @@ namespace SafeExamBrowser.Client
terminationActivator.Resume(); 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() private bool TryInitiateShutdown()
{ {
var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash); var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash);

View file

@ -23,7 +23,6 @@ using SafeExamBrowser.Communication.Contracts.Proxies;
using SafeExamBrowser.Communication.Hosts; using SafeExamBrowser.Communication.Hosts;
using SafeExamBrowser.Communication.Proxies; using SafeExamBrowser.Communication.Proxies;
using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Configuration.Cryptography;
using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.OperationModel; using SafeExamBrowser.Core.OperationModel;
@ -32,13 +31,13 @@ using SafeExamBrowser.I18n;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging; using SafeExamBrowser.Logging;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Processes; using SafeExamBrowser.Monitoring.Applications;
using SafeExamBrowser.Monitoring.Contracts.Windows; using SafeExamBrowser.Monitoring.Contracts.Applications;
using SafeExamBrowser.Monitoring.Display; using SafeExamBrowser.Monitoring.Display;
using SafeExamBrowser.Monitoring.Keyboard; using SafeExamBrowser.Monitoring.Keyboard;
using SafeExamBrowser.Monitoring.Mouse; using SafeExamBrowser.Monitoring.Mouse;
using SafeExamBrowser.Monitoring.Processes; using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Monitoring.Windows; using SafeExamBrowser.Settings.UserInterface;
using SafeExamBrowser.SystemComponents; using SafeExamBrowser.SystemComponents;
using SafeExamBrowser.SystemComponents.Audio; using SafeExamBrowser.SystemComponents.Audio;
using SafeExamBrowser.SystemComponents.Contracts; using SafeExamBrowser.SystemComponents.Contracts;
@ -52,7 +51,6 @@ using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
using Desktop = SafeExamBrowser.UserInterface.Desktop; using Desktop = SafeExamBrowser.UserInterface.Desktop;
using Mobile = SafeExamBrowser.UserInterface.Mobile; using Mobile = SafeExamBrowser.UserInterface.Mobile;
using SafeExamBrowser.Settings.Logging;
namespace SafeExamBrowser.Client namespace SafeExamBrowser.Client
{ {
@ -60,17 +58,18 @@ namespace SafeExamBrowser.Client
{ {
private Guid authenticationToken; private Guid authenticationToken;
private ClientConfiguration configuration; private ClientConfiguration configuration;
private ClientContext context;
private string logFilePath; private string logFilePath;
private LogLevel logLevel; private LogLevel logLevel;
private string runtimeHostUri; private string runtimeHostUri;
private UserInterfaceMode uiMode; private UserInterfaceMode uiMode;
private IActionCenter actionCenter; private IActionCenter actionCenter;
private IApplicationMonitor applicationMonitor;
private IBrowserApplication browser; private IBrowserApplication browser;
private IClientHost clientHost; private IClientHost clientHost;
private ILogger logger; private ILogger logger;
private IMessageBox messageBox; private IMessageBox messageBox;
private IProcessMonitor processMonitor;
private INativeMethods nativeMethods; private INativeMethods nativeMethods;
private IRuntimeProxy runtimeProxy; private IRuntimeProxy runtimeProxy;
private ISystemInfo systemInfo; private ISystemInfo systemInfo;
@ -79,7 +78,6 @@ namespace SafeExamBrowser.Client
private IText text; private IText text;
private ITextResource textResource; private ITextResource textResource;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
private IWindowMonitor windowMonitor;
internal IClientController ClientController { get; private set; } internal IClientController ClientController { get; private set; }
@ -96,13 +94,13 @@ namespace SafeExamBrowser.Client
InitializeText(); InitializeText();
actionCenter = BuildActionCenter(); actionCenter = BuildActionCenter();
applicationMonitor = new ApplicationMonitor(new ModuleLogger(logger, nameof(ApplicationMonitor)), nativeMethods);
context = new ClientContext();
messageBox = BuildMessageBox(); messageBox = BuildMessageBox();
processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods);
uiFactory = BuildUserInterfaceFactory(); uiFactory = BuildUserInterfaceFactory();
runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client);
taskbar = BuildTaskbar(); taskbar = BuildTaskbar();
terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator))); 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 displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods, systemInfo);
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); 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 I18nOperation(logger, text, textResource));
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, authenticationToken)); 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 DelegateOperation(UpdateAppConfig));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation));
operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildApplicationOperation));
operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation));
operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar)); operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation)); operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
@ -130,20 +127,19 @@ namespace SafeExamBrowser.Client
ClientController = new ClientController( ClientController = new ClientController(
actionCenter, actionCenter,
applicationMonitor,
displayMonitor, displayMonitor,
explorerShell, explorerShell,
hashAlgorithm, hashAlgorithm,
logger, logger,
messageBox, messageBox,
sequence, sequence,
processMonitor,
runtimeProxy, runtimeProxy,
shutdown, shutdown,
taskbar, taskbar,
terminationActivator, terminationActivator,
text, text,
uiFactory, uiFactory);
windowMonitor);
} }
internal void LogStartupInformation() internal void LogStartupInformation()
@ -202,6 +198,11 @@ namespace SafeExamBrowser.Client
textResource = new XmlTextResource(path); textResource = new XmlTextResource(path);
} }
private IOperation BuildApplicationOperation()
{
return new ApplicationOperation(applicationMonitor, context, logger);
}
private IOperation BuildBrowserOperation() private IOperation BuildBrowserOperation()
{ {
var moduleLogger = new ModuleLogger(logger, nameof(BrowserApplication)); var moduleLogger = new ModuleLogger(logger, nameof(BrowserApplication));
@ -238,25 +239,20 @@ namespace SafeExamBrowser.Client
private IOperation BuildKeyboardInterceptorOperation() private IOperation BuildKeyboardInterceptorOperation()
{ {
var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor))); var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor)), nativeMethods);
var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods); var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger);
return operation; return operation;
} }
private IOperation BuildMouseInterceptorOperation() private IOperation BuildMouseInterceptorOperation()
{ {
var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse); var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse, nativeMethods);
var operation = new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods); var operation = new MouseInterceptorOperation(logger, mouseInterceptor);
return operation; return operation;
} }
private IOperation BuildProcessMonitorOperation()
{
return new ProcessMonitorOperation(logger, processMonitor, configuration.Settings);
}
private IOperation BuildShellOperation() private IOperation BuildShellOperation()
{ {
var aboutInfo = new AboutNotificationInfo(text); var aboutInfo = new AboutNotificationInfo(text);
@ -295,11 +291,6 @@ namespace SafeExamBrowser.Client
return operation; return operation;
} }
private IOperation BuildWindowMonitorOperation()
{
return new WindowMonitorOperation(configuration.Settings.KioskMode, logger, windowMonitor);
}
private IActionCenter BuildActionCenter() private IActionCenter BuildActionCenter()
{ {
switch (uiMode) 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 namespace SafeExamBrowser.Client.Operations
{ {
internal class ConfigurationOperation : IOperation internal class ConfigurationOperation : ClientOperation
{ {
private ClientConfiguration configuration; private ClientConfiguration configuration;
private ILogger logger; private ILogger logger;
private IRuntimeProxy runtime; private IRuntimeProxy runtime;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged; 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.configuration = configuration;
this.logger = logger; this.logger = logger;
this.runtime = runtime; this.runtime = runtime;
} }
public OperationResult Perform() public override OperationResult Perform()
{ {
logger.Info("Initializing application configuration..."); logger.Info("Initializing application configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
@ -43,6 +44,9 @@ namespace SafeExamBrowser.Client.Operations
configuration.SessionId = config.SessionId; configuration.SessionId = config.SessionId;
configuration.Settings = config.Settings; configuration.Settings = config.Settings;
Context.AppConfig = config.AppConfig;
Context.Settings = config.Settings;
logger.Info("Successfully retrieved the application configuration from the runtime."); logger.Info("Successfully retrieved the application configuration from the runtime.");
logger.Info($" -> Client-ID: {configuration.AppConfig.ClientId}"); logger.Info($" -> Client-ID: {configuration.AppConfig.ClientId}");
logger.Info($" -> Runtime-ID: {configuration.AppConfig.RuntimeId}"); logger.Info($" -> Runtime-ID: {configuration.AppConfig.RuntimeId}");
@ -51,7 +55,7 @@ namespace SafeExamBrowser.Client.Operations
return OperationResult.Success; return OperationResult.Success;
} }
public OperationResult Revert() public override OperationResult Revert()
{ {
return OperationResult.Success; 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.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard; using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {
@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations
{ {
private IKeyboardInterceptor keyboardInterceptor; private IKeyboardInterceptor keyboardInterceptor;
private ILogger logger; private ILogger logger;
private INativeMethods nativeMethods;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged; public event StatusChangedEventHandler StatusChanged;
public KeyboardInterceptorOperation( public KeyboardInterceptorOperation(IKeyboardInterceptor keyboardInterceptor, ILogger logger)
IKeyboardInterceptor keyboardInterceptor,
ILogger logger,
INativeMethods nativeMethods)
{ {
this.keyboardInterceptor = keyboardInterceptor; this.keyboardInterceptor = keyboardInterceptor;
this.logger = logger; this.logger = logger;
this.nativeMethods = nativeMethods;
} }
public OperationResult Perform() public OperationResult Perform()
@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Starting keyboard interception..."); logger.Info("Starting keyboard interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StartKeyboardInterception); StatusChanged?.Invoke(TextKey.OperationStatus_StartKeyboardInterception);
nativeMethods.RegisterKeyboardHook(keyboardInterceptor); keyboardInterceptor.Start();
return OperationResult.Success; return OperationResult.Success;
} }
@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Stopping keyboard interception..."); logger.Info("Stopping keyboard interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception); StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception);
nativeMethods.DeregisterKeyboardHook(keyboardInterceptor); keyboardInterceptor.Stop();
return OperationResult.Success; return OperationResult.Success;
} }

View file

@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Client.Operations namespace SafeExamBrowser.Client.Operations
{ {
@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations
{ {
private ILogger logger; private ILogger logger;
private IMouseInterceptor mouseInterceptor; private IMouseInterceptor mouseInterceptor;
private INativeMethods nativeMethods;
public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
public event StatusChangedEventHandler StatusChanged; public event StatusChangedEventHandler StatusChanged;
public MouseInterceptorOperation( public MouseInterceptorOperation(ILogger logger, IMouseInterceptor mouseInterceptor)
ILogger logger,
IMouseInterceptor mouseInterceptor,
INativeMethods nativeMethods)
{ {
this.logger = logger; this.logger = logger;
this.mouseInterceptor = mouseInterceptor; this.mouseInterceptor = mouseInterceptor;
this.nativeMethods = nativeMethods;
} }
public OperationResult Perform() public OperationResult Perform()
@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Starting mouse interception..."); logger.Info("Starting mouse interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StartMouseInterception); StatusChanged?.Invoke(TextKey.OperationStatus_StartMouseInterception);
nativeMethods.RegisterMouseHook(mouseInterceptor); mouseInterceptor.Start();
return OperationResult.Success; return OperationResult.Success;
} }
@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations
logger.Info("Stopping mouse interception..."); logger.Info("Stopping mouse interception...");
StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception); StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception);
nativeMethods.DeregisterMouseHook(mouseInterceptor); mouseInterceptor.Stop();
return OperationResult.Success; 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>
<ItemGroup> <ItemGroup>
<Compile Include="App.cs" /> <Compile Include="App.cs" />
<Compile Include="ClientContext.cs" />
<Compile Include="ClientController.cs" /> <Compile Include="ClientController.cs" />
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" /> <Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
<Compile Include="Operations\ClientOperation.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" /> <Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\Events\ProcessTerminationEventArgs.cs" />
<Compile Include="Operations\RuntimeConnectionOperation.cs" /> <Compile Include="Operations\RuntimeConnectionOperation.cs" />
<Compile Include="Communication\ClientHost.cs" /> <Compile Include="Communication\ClientHost.cs" />
<Compile Include="CompositionRoot.cs" /> <Compile Include="CompositionRoot.cs" />
@ -88,9 +91,8 @@
<Compile Include="Operations\DisplayMonitorOperation.cs" /> <Compile Include="Operations\DisplayMonitorOperation.cs" />
<Compile Include="Operations\KeyboardInterceptorOperation.cs" /> <Compile Include="Operations\KeyboardInterceptorOperation.cs" />
<Compile Include="Operations\MouseInterceptorOperation.cs" /> <Compile Include="Operations\MouseInterceptorOperation.cs" />
<Compile Include="Operations\ProcessMonitorOperation.cs" /> <Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" /> <Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\WindowMonitorOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs"> <Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType> <SubType>Code</SubType>
</Compile> </Compile>
@ -220,6 +222,7 @@
<ItemGroup> <ItemGroup>
<Resource Include="SafeExamBrowser.ico" /> <Resource Include="SafeExamBrowser.ico" />
</ItemGroup> </ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<PropertyGroup> <PropertyGroup>
<PostBuildEvent>robocopy "$(SolutionDir)SafeExamBrowser.Browser\bin\$(PlatformName)\$(ConfigurationName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)" /e /np <PostBuildEvent>robocopy "$(SolutionDir)SafeExamBrowser.Browser\bin\$(PlatformName)\$(ConfigurationName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)" /e /np

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts namespace SafeExamBrowser.Configuration.Contracts
{ {
@ -29,6 +30,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary> /// <summary>
/// The application settings to be used by the client. /// The application settings to be used by the client.
/// </summary> /// </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.Cryptography;
using SafeExamBrowser.Configuration.Contracts.DataFormats; using SafeExamBrowser.Configuration.Contracts.DataFormats;
using SafeExamBrowser.Configuration.Contracts.DataResources; using SafeExamBrowser.Configuration.Contracts.DataResources;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts namespace SafeExamBrowser.Configuration.Contracts
{ {
@ -36,7 +37,7 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary> /// <summary>
/// Loads the default settings. /// Loads the default settings.
/// </summary> /// </summary>
Settings.ApplicationSettings LoadDefaultSettings(); AppSettings LoadDefaultSettings();
/// <summary> /// <summary>
/// Registers the specified <see cref="IDataParser"/> to be used to parse configuration data. /// Registers the specified <see cref="IDataParser"/> to be used to parse configuration data.
@ -61,6 +62,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary> /// <summary>
/// Attempts to load settings from the specified resource. /// Attempts to load settings from the specified resource.
/// </summary> /// </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 System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts namespace SafeExamBrowser.Configuration.Contracts
{ {
@ -29,7 +30,7 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary> /// <summary>
/// The application settings to be used by the service. /// The application settings to be used by the service.
/// </summary> /// </summary>
public Settings.ApplicationSettings Settings { get; set; } public AppSettings Settings { get; set; }
/// <summary> /// <summary>
/// The user name of the currently logged in user. /// The user name of the currently logged in user.

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Settings;
namespace SafeExamBrowser.Configuration.Contracts namespace SafeExamBrowser.Configuration.Contracts
{ {
@ -16,7 +17,7 @@ namespace SafeExamBrowser.Configuration.Contracts
public class SessionConfiguration public class SessionConfiguration
{ {
/// <summary> /// <summary>
/// The active application configuration for this session. /// The application configuration for this session.
/// </summary> /// </summary>
public AppConfig AppConfig { get; set; } public AppConfig AppConfig { get; set; }
@ -33,6 +34,6 @@ namespace SafeExamBrowser.Configuration.Contracts
/// <summary> /// <summary>
/// The settings used for this session. /// The settings used for this session.
/// </summary> /// </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 internal partial class DataMapper
{ {
private void MapInitialVolumeLevel(ApplicationSettings settings, object value) private void MapInitialVolumeLevel(AppSettings settings, object value)
{ {
if (value is int volume) 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) 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) if (value is bool initialize)
{ {

View file

@ -15,7 +15,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper internal partial class DataMapper
{ {
private void MapAllowAddressBar(ApplicationSettings settings, object value) private void MapAllowAddressBar(AppSettings settings, object value)
{ {
if (value is bool allow) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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; const int ALLOW = 1;
@ -132,7 +132,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
if (item is IDictionary<string, object> ruleData) 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(); 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; 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) 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) 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; const int DEFAULT = 0;

View file

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

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper internal partial class DataMapper
{ {
private void MapAdminPasswordHash(ApplicationSettings settings, object value) private void MapAdminPasswordHash(AppSettings settings, object value)
{ {
if (value is string hash) 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); 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; 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) 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) if (value is string url)
{ {

View file

@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper internal partial class DataMapper
{ {
private void MapEnableAltEsc(ApplicationSettings settings, object value) private void MapEnableAltEsc(AppSettings settings, object value)
{ {
if (value is bool enabled) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) 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) if (value is bool enabled)
{ {

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper 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 hasCreateNewDesktop = rawData.TryGetValue(Keys.Security.KioskModeCreateNewDesktop, out var createNewDesktop);
var hasDisableExplorerShell = rawData.TryGetValue(Keys.Security.KioskModeDisableExplorerShell, out var disableExplorerShell); 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 WARN = 1;
const int FORCE = 2; const int FORCE = 2;

View file

@ -13,7 +13,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper internal partial class DataMapper
{ {
private void MapApplicationLogButton(ApplicationSettings settings, object value) private void MapApplicationLogButton(AppSettings settings, object value)
{ {
if (value is bool show) 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) 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) 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) 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) 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) if (value is bool mobile)
{ {

View file

@ -13,10 +13,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal partial class DataMapper 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) foreach (var item in rawData)
{ {
MapApplicationSettings(item.Key, item.Value, settings);
MapAudioSettings(item.Key, item.Value, settings); MapAudioSettings(item.Key, item.Value, settings);
MapBrowserSettings(item.Key, item.Value, settings); MapBrowserSettings(item.Key, item.Value, settings);
MapConfigurationFileSettings(item.Key, item.Value, settings); MapConfigurationFileSettings(item.Key, item.Value, settings);
@ -31,7 +32,20 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
MapUserAgentMode(rawData, settings); 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) 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) 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) 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) 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) 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) 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) switch (key)
{ {

View file

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

View file

@ -10,12 +10,21 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
{ {
internal static class Keys internal static class Keys
{ {
internal const int WINDOWS = 1;
internal static class AdditionalResources internal static class AdditionalResources
{ {
} }
internal static class Applications 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 internal static class Audio

View file

@ -100,7 +100,7 @@ namespace SafeExamBrowser.Configuration
return dataValues.InitializeSessionConfiguration(); return dataValues.InitializeSessionConfiguration();
} }
public ApplicationSettings LoadDefaultSettings() public AppSettings LoadDefaultSettings()
{ {
return dataValues.LoadDefaultSettings(); return dataValues.LoadDefaultSettings();
} }
@ -125,7 +125,7 @@ namespace SafeExamBrowser.Configuration
resourceSavers.Add(saver); 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}'..."); logger.Info($"Attempting to load '{resource}'...");

View file

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

View file

@ -6,7 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * 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> /// <summary>
/// Indicates that the Windows explorer process has started. /// 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 public interface IKeyboardInterceptor
{ {
/// <summary> /// <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> /// </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 public interface IMouseInterceptor
{ {
/// <summary> /// <summary>
/// Returns <c>true</c> if the given button should be blocked, otherwise <c>false</c>. /// Starts intercepting mouse input.
/// </summary> /// </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" /> <Reference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="Applications\RunningApplicationInfo.cs" />
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" /> <Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
<Compile Include="Processes\Events\ExplorerStartedEventHandler.cs" /> <Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
<Compile Include="Windows\Events\WindowChangedEventHandler.cs" />
<Compile Include="Display\IDisplayMonitor.cs" /> <Compile Include="Display\IDisplayMonitor.cs" />
<Compile Include="Keyboard\IKeyboardInterceptor.cs" /> <Compile Include="Keyboard\IKeyboardInterceptor.cs" />
<Compile Include="Mouse\MouseButtonState.cs" />
<Compile Include="Mouse\IMouseInterceptor.cs" /> <Compile Include="Mouse\IMouseInterceptor.cs" />
<Compile Include="Processes\IProcessMonitor.cs" /> <Compile Include="Applications\InitializationResult.cs" />
<Compile Include="Windows\IWindowMonitor.cs" /> <Compile Include="Applications\IApplicationMonitor.cs" />
<Compile Include="Keyboard\KeyModifier.cs" />
<Compile Include="Keyboard\KeyState.cs" />
<Compile Include="Mouse\MouseButton.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </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" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </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;
using System.Linq; using System.Linq;
using System.Windows.Input; using System.Windows.Input;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Keyboard; using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.Monitoring.Keyboard namespace SafeExamBrowser.Monitoring.Keyboard
{ {
public class KeyboardInterceptor : IKeyboardInterceptor public class KeyboardInterceptor : IKeyboardInterceptor
{ {
private KeyboardSettings settings; private Guid? hookId;
private ILogger logger; 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.logger = logger;
this.nativeMethods = nativeMethods;
this.settings = settings; 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 block = false;
var key = KeyInterop.KeyFromVirtualKey(keyCode); var key = KeyInterop.KeyFromVirtualKey(keyCode);

View file

@ -6,24 +6,43 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
using SafeExamBrowser.Settings.Monitoring; using System;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.Settings.Monitoring;
using SafeExamBrowser.WindowsApi.Contracts;
using SafeExamBrowser.WindowsApi.Contracts.Events;
namespace SafeExamBrowser.Monitoring.Mouse namespace SafeExamBrowser.Monitoring.Mouse
{ {
public class MouseInterceptor : IMouseInterceptor public class MouseInterceptor : IMouseInterceptor
{ {
private Guid? hookId;
private ILogger logger; private ILogger logger;
private INativeMethods nativeMethods;
private MouseSettings settings; private MouseSettings settings;
public MouseInterceptor(ILogger logger, MouseSettings settings) public MouseInterceptor(ILogger logger, MouseSettings settings, INativeMethods nativeMethods)
{ {
this.logger = logger; this.logger = logger;
this.nativeMethods = nativeMethods;
this.settings = settings; 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; 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="Display\DisplayMonitor.cs" />
<Compile Include="Keyboard\KeyboardInterceptor.cs" /> <Compile Include="Keyboard\KeyboardInterceptor.cs" />
<Compile Include="Mouse\MouseInterceptor.cs" /> <Compile Include="Mouse\MouseInterceptor.cs" />
<Compile Include="Processes\ProcessMonitor.cs" /> <Compile Include="Applications\ApplicationMonitor.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Windows\WindowMonitor.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj"> <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() public void MustHandleConfigurationRequestCorrectly()
{ {
var args = default(ClientConfigurationEventArgs); 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.AllowConnection = true;
sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; }; sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; };

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -107,7 +107,7 @@ namespace SafeExamBrowser.Runtime.Operations
{ {
var currentPassword = default(string); var currentPassword = default(string);
var passwordParams = default(PasswordParameters); var passwordParams = default(PasswordParameters);
var settings = default(ApplicationSettings); var settings = default(AppSettings);
var status = default(LoadStatus?); var status = default(LoadStatus?);
if (source == UriSource.CommandLine) 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) if (status == LoadStatus.LoadWithBrowser || status == LoadStatus.Success)
{ {
@ -218,7 +218,7 @@ namespace SafeExamBrowser.Runtime.Operations
return OperationResult.Failed; 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 }; 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/. * 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;
using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts;
namespace SafeExamBrowser.Runtime.Operations namespace SafeExamBrowser.Runtime.Operations

View file

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

View file

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

View file

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

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Settings.Applications;
using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.Settings.Browser;
using SafeExamBrowser.Settings.Logging; using SafeExamBrowser.Settings.Logging;
using SafeExamBrowser.Settings.Monitoring; using SafeExamBrowser.Settings.Monitoring;
@ -20,7 +21,7 @@ namespace SafeExamBrowser.Settings
/// Defines all settings for the application. /// Defines all settings for the application.
/// </summary> /// </summary>
[Serializable] [Serializable]
public class ApplicationSettings public class AppSettings
{ {
/// <summary> /// <summary>
/// All action center-related settings. /// All action center-related settings.
@ -37,6 +38,11 @@ namespace SafeExamBrowser.Settings
/// </summary> /// </summary>
public bool AllowApplicationLogAccess { get; set; } public bool AllowApplicationLogAccess { get; set; }
/// <summary>
/// All settings related to third-party applications.
/// </summary>
public ApplicationSettings Applications { get; set; }
/// <summary> /// <summary>
/// All audio-related settings. /// All audio-related settings.
/// </summary> /// </summary>
@ -92,9 +98,10 @@ namespace SafeExamBrowser.Settings
/// </summary> /// </summary>
public UserInterfaceMode UserInterfaceMode { get; set; } public UserInterfaceMode UserInterfaceMode { get; set; }
public ApplicationSettings() public AppSettings()
{ {
ActionCenter = new ActionCenterSettings(); ActionCenter = new ActionCenterSettings();
Applications = new ApplicationSettings();
Audio = new AudioSettings(); Audio = new AudioSettings();
Browser = new BrowserSettings(); Browser = new BrowserSettings();
Keyboard = new KeyboardSettings(); 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; using System;
namespace SafeExamBrowser.Monitoring.Contracts.Windows.Events namespace SafeExamBrowser.Settings.Applications
{ {
/// <summary> /// <summary>
/// Indicates that the input focus has changed to the window with the specified handle. /// TODO
/// </summary> /// </summary>
public delegate void WindowChangedEventHandler(IntPtr window); [Serializable]
public class WhitelistApplication
{
}
} }

View file

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

View file

@ -8,10 +8,10 @@
using System; using System;
namespace SafeExamBrowser.Monitoring.Contracts.Keyboard namespace SafeExamBrowser.WindowsApi.Contracts.Events
{ {
/// <summary> /// <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> /// </summary>
[Flags] [Flags]
public enum KeyModifier public enum KeyModifier

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Monitoring.Contracts.Keyboard namespace SafeExamBrowser.WindowsApi.Contracts.Events
{ {
/// <summary> /// <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> /// </summary>
public enum KeyState 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Monitoring.Contracts.Mouse namespace SafeExamBrowser.WindowsApi.Contracts.Events
{ {
/// <summary> /// <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> /// </summary>
public enum MouseButton public enum MouseButton
{ {

View file

@ -6,10 +6,10 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.Monitoring.Contracts.Mouse namespace SafeExamBrowser.WindowsApi.Contracts.Events
{ {
/// <summary> /// <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> /// </summary>
public enum MouseButtonState 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/. * file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/ */
namespace SafeExamBrowser.WindowsApi.Contracts namespace SafeExamBrowser.WindowsApi.Contracts
{ {
/// <summary> /// <summary>

View file

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

View file

@ -53,6 +53,12 @@
<Reference Include="Microsoft.CSharp" /> <Reference Include="Microsoft.CSharp" />
</ItemGroup> </ItemGroup>
<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\ProcessTerminatedEventHandler.cs" />
<Compile Include="Events\SystemEventCallback.cs" /> <Compile Include="Events\SystemEventCallback.cs" />
<Compile Include="Events\TerminationActivatorEventHandler.cs" /> <Compile Include="Events\TerminationActivatorEventHandler.cs" />
@ -66,11 +72,5 @@
<Compile Include="ITerminationActivator.cs" /> <Compile Include="ITerminationActivator.cs" />
<Compile Include="Properties\AssemblyInfo.cs" /> <Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup> </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" /> <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project> </Project>

View file

@ -8,8 +8,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SafeExamBrowser.Monitoring.Contracts.Keyboard;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
@ -18,14 +18,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks
internal class KeyboardHook internal class KeyboardHook
{ {
private bool altPressed, ctrlPressed; private bool altPressed, ctrlPressed;
private KeyboardHookCallback callback;
private IntPtr handle;
private HookDelegate hookDelegate; private HookDelegate hookDelegate;
internal IntPtr Handle { get; private set; } internal Guid Id { get; private set; }
internal IKeyboardInterceptor Interceptor { get; private set; }
internal KeyboardHook(IKeyboardInterceptor interceptor) internal KeyboardHook(KeyboardHookCallback callback)
{ {
Interceptor = interceptor; this.callback = callback;
this.Id = Guid.NewGuid();
} }
internal void Attach() 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. // 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! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelKeyboardProc); 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() internal bool Detach()
{ {
return User32.UnhookWindowsHookEx(Handle); return User32.UnhookWindowsHookEx(handle);
} }
private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam) private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam)
@ -55,13 +56,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks
var modifier = GetModifiers(keyData, wParam.ToInt32()); var modifier = GetModifiers(keyData, wParam.ToInt32());
var state = GetState(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 (IntPtr) 1;
} }
} }
return User32.CallNextHookEx(Handle, nCode, wParam, lParam); return User32.CallNextHookEx(handle, nCode, wParam, lParam);
} }
private KeyState GetState(int wParam) private KeyState GetState(int wParam)

View file

@ -8,8 +8,8 @@
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using SafeExamBrowser.Monitoring.Contracts.Mouse;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
using SafeExamBrowser.WindowsApi.Types; using SafeExamBrowser.WindowsApi.Types;
@ -17,14 +17,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks
{ {
internal class MouseHook internal class MouseHook
{ {
private MouseHookCallback callback;
private IntPtr handle;
private HookDelegate hookDelegate; private HookDelegate hookDelegate;
internal IntPtr Handle { get; private set; } internal Guid Id { get; private set; }
internal IMouseInterceptor Interceptor { get; private set; }
internal MouseHook(IMouseInterceptor interceptor) internal MouseHook(MouseHookCallback callback)
{ {
Interceptor = interceptor; this.callback = callback;
this.Id = Guid.NewGuid();
} }
internal void Attach() 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. // 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! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
hookDelegate = new HookDelegate(LowLevelMouseProc); 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() internal bool Detach()
{ {
return User32.UnhookWindowsHookEx(Handle); return User32.UnhookWindowsHookEx(handle);
} }
private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam) private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam)
@ -54,13 +55,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks
var button = GetButton(wParam.ToInt32()); var button = GetButton(wParam.ToInt32());
var state = GetState(wParam.ToInt32()); var state = GetState(wParam.ToInt32());
if (Interceptor.Block(button, state)) if (callback(button, state))
{ {
return (IntPtr) 1; return (IntPtr) 1;
} }
} }
return User32.CallNextHookEx(Handle, nCode, wParam, lParam); return User32.CallNextHookEx(handle, nCode, wParam, lParam);
} }
private bool Ignore(int wParam) private bool Ignore(int wParam)

View file

@ -8,8 +8,8 @@
using System; using System;
using System.Threading; using System.Threading;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Constants;
using SafeExamBrowser.WindowsApi.Contracts.Events;
using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Delegates;
namespace SafeExamBrowser.WindowsApi.Hooks namespace SafeExamBrowser.WindowsApi.Hooks
@ -21,8 +21,8 @@ namespace SafeExamBrowser.WindowsApi.Hooks
private bool detachSuccess; private bool detachSuccess;
private EventDelegate eventDelegate; private EventDelegate eventDelegate;
private uint eventId; private uint eventId;
private IntPtr handle;
internal IntPtr Handle { get; private set; }
internal Guid Id { get; private set; } internal Guid Id { get; private set; }
public SystemHook(SystemEventCallback callback, uint eventId) 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. // 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! // Not doing so will result in a <c>CallbackOnCollectedDelegate</c> error and subsequent application crash!
eventDelegate = new EventDelegate(LowLevelSystemProc); 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() internal void AwaitDetach()
{ {
detachEvent.WaitOne(); detachEvent.WaitOne();
detachSuccess = User32.UnhookWinEvent(Handle); detachSuccess = User32.UnhookWinEvent(handle);
detachResultAvailableEvent.Set(); detachResultAvailableEvent.Set();
} }

View file

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

View file

@ -96,10 +96,6 @@
<Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project> <Project>{64ea30fb-11d4-436a-9c2b-88566285363e}</Project>
<Name>SafeExamBrowser.Logging.Contracts</Name> <Name>SafeExamBrowser.Logging.Contracts</Name>
</ProjectReference> </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"> <ProjectReference Include="..\SafeExamBrowser.UserInterface.Contracts\SafeExamBrowser.UserInterface.Contracts.csproj">
<Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project> <Project>{c7889e97-6ff6-4a58-b7cb-521ed276b316}</Project>
<Name>SafeExamBrowser.UserInterface.Contracts</Name> <Name>SafeExamBrowser.UserInterface.Contracts</Name>