diff --git a/SafeExamBrowser.Client.Contracts/IClientController.cs b/SafeExamBrowser.Client.Contracts/IClientController.cs index 2e974175..b9fe22b1 100644 --- a/SafeExamBrowser.Client.Contracts/IClientController.cs +++ b/SafeExamBrowser.Client.Contracts/IClientController.cs @@ -10,6 +10,7 @@ using System; using SafeExamBrowser.Browser.Contracts; using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Configuration.Contracts; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Client.Contracts { @@ -41,7 +42,7 @@ namespace SafeExamBrowser.Client.Contracts /// /// The settings to be used during application execution. /// - Settings.ApplicationSettings Settings { set; } + AppSettings Settings { set; } /// /// Reverts any changes, releases all used resources and terminates the client. diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index e312ac27..39206dba 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -17,14 +17,13 @@ using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; -using SafeExamBrowser.Settings; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Display; -using SafeExamBrowser.Monitoring.Contracts.Processes; -using SafeExamBrowser.Monitoring.Contracts.Windows; +using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -38,6 +37,7 @@ namespace SafeExamBrowser.Client.UnitTests { private AppConfig appConfig; private Mock actionCenter; + private Mock applicationMonitor; private Mock browserController; private Mock clientHost; private Mock displayMonitor; @@ -45,17 +45,15 @@ namespace SafeExamBrowser.Client.UnitTests private Mock hashAlgorithm; private Mock logger; private Mock messageBox; - private Mock processMonitor; private Mock operationSequence; private Mock runtimeProxy; private Guid sessionId; - private ApplicationSettings settings; + private AppSettings settings; private Mock shutdown; private Mock taskbar; private Mock terminationActivator; private Mock text; private Mock uiFactory; - private Mock windowMonitor; private ClientController sut; @@ -64,6 +62,7 @@ namespace SafeExamBrowser.Client.UnitTests { appConfig = new AppConfig(); actionCenter = new Mock(); + applicationMonitor = new Mock(); browserController = new Mock(); clientHost = new Mock(); displayMonitor = new Mock(); @@ -71,17 +70,15 @@ namespace SafeExamBrowser.Client.UnitTests hashAlgorithm = new Mock(); logger = new Mock(); messageBox = new Mock(); - processMonitor = new Mock(); operationSequence = new Mock(); runtimeProxy = new Mock(); sessionId = Guid.NewGuid(); - settings = new ApplicationSettings(); + settings = new AppSettings(); shutdown = new Mock(); taskbar = new Mock(); terminationActivator = new Mock(); text = new Mock(); uiFactory = new Mock(); - windowMonitor = new Mock(); operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success); runtimeProxy.Setup(r => r.InformClientReady()).Returns(new CommunicationResult(true)); @@ -89,20 +86,19 @@ namespace SafeExamBrowser.Client.UnitTests sut = new ClientController( actionCenter.Object, + applicationMonitor.Object, displayMonitor.Object, explorerShell.Object, hashAlgorithm.Object, logger.Object, messageBox.Object, operationSequence.Object, - processMonitor.Object, runtimeProxy.Object, shutdown.Object, taskbar.Object, terminationActivator.Object, text.Object, - uiFactory.Object, - windowMonitor.Object); + uiFactory.Object); sut.AppConfig = appConfig; sut.Browser = browserController.Object; @@ -111,6 +107,30 @@ namespace SafeExamBrowser.Client.UnitTests sut.Settings = settings; } + [TestMethod] + public void ApplicationMonitor_MustHandleExplorerStartCorrectly() + { + var order = 0; + var shell = 0; + var workingArea = 0; + var bounds = 0; + + explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order); + displayMonitor.Setup(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order); + taskbar.Setup(t => t.InitializeBounds()).Callback(() => bounds = ++order); + + sut.TryStart(); + applicationMonitor.Raise(p => p.ExplorerStarted += null); + + explorerShell.Verify(p => p.Terminate(), Times.Once); + displayMonitor.Verify(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight()), Times.Once); + taskbar.Verify(t => t.InitializeBounds(), Times.Once); + + Assert.IsTrue(shell == 1); + Assert.IsTrue(workingArea == 2); + Assert.IsTrue(bounds == 3); + } + [TestMethod] public void Communication_MustCorrectlyHandleMessageBoxRequest() { @@ -265,30 +285,6 @@ namespace SafeExamBrowser.Client.UnitTests splashScreen.Verify(s => s.UpdateStatus(It.Is(k => k == key), It.IsAny()), Times.Once); } - [TestMethod] - public void ProcessMonitor_MustHandleExplorerStartCorrectly() - { - var order = 0; - var shell = 0; - var workingArea = 0; - var bounds = 0; - - explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order); - displayMonitor.Setup(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight())).Callback(() => workingArea = ++order); - taskbar.Setup(t => t.InitializeBounds()).Callback(() => bounds = ++order); - - sut.TryStart(); - processMonitor.Raise(p => p.ExplorerStarted += null); - - explorerShell.Verify(p => p.Terminate(), Times.Once); - displayMonitor.Verify(w => w.InitializePrimaryDisplay(taskbar.Object.GetAbsoluteHeight()), Times.Once); - taskbar.Verify(t => t.InitializeBounds(), Times.Once); - - Assert.IsTrue(shell == 1); - Assert.IsTrue(workingArea == 2); - Assert.IsTrue(bounds == 3); - } - [TestMethod] public void Reconfiguration_MustDenyIfInExamMode() { @@ -642,67 +638,5 @@ namespace SafeExamBrowser.Client.UnitTests terminationActivator.Verify(t => t.Resume(), Times.Once); runtimeProxy.Verify(p => p.RequestShutdown(), Times.Once); } - - [TestMethod] - public void WindowMonitor_MustHandleAllowedWindowChangeCorrectly() - { - var window = new IntPtr(12345); - - processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(true); - - sut.TryStart(); - windowMonitor.Raise(w => w.WindowChanged += null, window); - - processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once); - windowMonitor.Verify(w => w.Hide(window), Times.Never); - windowMonitor.Verify(w => w.Close(window), Times.Never); - } - - [TestMethod] - public void WindowMonitor_MustHandleUnallowedWindowHideCorrectly() - { - var order = 0; - var belongs = 0; - var hide = 0; - var window = new IntPtr(12345); - - processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(false).Callback(() => belongs = ++order); - windowMonitor.Setup(w => w.Hide(window)).Returns(true).Callback(() => hide = ++order); - - sut.TryStart(); - windowMonitor.Raise(w => w.WindowChanged += null, window); - - processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once); - windowMonitor.Verify(w => w.Hide(window), Times.Once); - windowMonitor.Verify(w => w.Close(window), Times.Never); - - Assert.IsTrue(belongs == 1); - Assert.IsTrue(hide == 2); - } - - [TestMethod] - public void WindowMonitor_MustHandleUnallowedWindowCloseCorrectly() - { - var order = 0; - var belongs = 0; - var hide = 0; - var close = 0; - var window = new IntPtr(12345); - - processMonitor.Setup(p => p.BelongsToAllowedProcess(window)).Returns(false).Callback(() => belongs = ++order); - windowMonitor.Setup(w => w.Hide(window)).Returns(false).Callback(() => hide = ++order); - windowMonitor.Setup(w => w.Close(window)).Callback(() => close = ++order); - - sut.TryStart(); - windowMonitor.Raise(w => w.WindowChanged += null, window); - - processMonitor.Verify(p => p.BelongsToAllowedProcess(window), Times.Once); - windowMonitor.Verify(w => w.Hide(window), Times.Once); - windowMonitor.Verify(w => w.Close(window), Times.Once); - - Assert.IsTrue(belongs == 1); - Assert.IsTrue(hide == 2); - Assert.IsTrue(close == 3); - } } } diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ApplicationOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ApplicationOperationTests.cs new file mode 100644 index 00000000..97813441 --- /dev/null +++ b/SafeExamBrowser.Client.UnitTests/Operations/ApplicationOperationTests.cs @@ -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(); + } + } +} diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ConfigurationOperationTests.cs index 24ec5469..20cfd1a8 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ConfigurationOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ConfigurationOperationTests.cs @@ -13,9 +13,9 @@ using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Communication.Contracts.Data; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Configuration.Contracts; -using SafeExamBrowser.Settings; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Client.UnitTests.Operations { @@ -23,6 +23,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations public class ConfigurationOperationTests { private ClientConfiguration configuration; + private ClientContext context; private Mock logger; private Mock runtime; private ConfigurationOperation sut; @@ -31,10 +32,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations public void Initialize() { configuration = new ClientConfiguration(); + context = new ClientContext(); logger = new Mock(); runtime = new Mock(); - sut = new ConfigurationOperation(configuration, logger.Object, runtime.Object); + sut = new ConfigurationOperation(configuration, context, logger.Object, runtime.Object); } [TestMethod] @@ -46,7 +48,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { AppConfig = new AppConfig(), SessionId = Guid.NewGuid(), - Settings = new ApplicationSettings() + Settings = new AppSettings() } }; diff --git a/SafeExamBrowser.Client.UnitTests/Operations/KeyboardInterceptorOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/KeyboardInterceptorOperationTests.cs index e49cd135..b1181644 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/KeyboardInterceptorOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/KeyboardInterceptorOperationTests.cs @@ -11,7 +11,6 @@ using Moq; using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Keyboard; -using SafeExamBrowser.WindowsApi.Contracts; namespace SafeExamBrowser.Client.UnitTests.Operations { @@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { private Mock keyboardInterceptorMock; private Mock loggerMock; - private Mock nativeMethodsMock; private KeyboardInterceptorOperation sut; @@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { keyboardInterceptorMock = new Mock(); loggerMock = new Mock(); - nativeMethodsMock = new Mock(); - sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object, nativeMethodsMock.Object); + sut = new KeyboardInterceptorOperation(keyboardInterceptorMock.Object, loggerMock.Object); } [TestMethod] @@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { sut.Perform(); - nativeMethodsMock.Verify(n => n.RegisterKeyboardHook(It.IsAny()), Times.Once); + keyboardInterceptorMock.Verify(i => i.Start(), Times.Once); + keyboardInterceptorMock.Verify(i => i.Stop(), Times.Never); } [TestMethod] @@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { sut.Revert(); - nativeMethodsMock.Verify(n => n.DeregisterKeyboardHook(It.IsAny()), Times.Once); + keyboardInterceptorMock.Verify(i => i.Start(), Times.Never); + keyboardInterceptorMock.Verify(i => i.Stop(), Times.Once); } } } diff --git a/SafeExamBrowser.Client.UnitTests/Operations/MouseInterceptorOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/MouseInterceptorOperationTests.cs index 73800b1f..89505bb3 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/MouseInterceptorOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/MouseInterceptorOperationTests.cs @@ -11,7 +11,6 @@ using Moq; using SafeExamBrowser.Client.Operations; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Mouse; -using SafeExamBrowser.WindowsApi.Contracts; namespace SafeExamBrowser.Client.UnitTests.Operations { @@ -20,7 +19,6 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { private Mock mouseInterceptorMock; private Mock loggerMock; - private Mock nativeMethodsMock; private MouseInterceptorOperation sut; @@ -29,9 +27,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { mouseInterceptorMock = new Mock(); loggerMock = new Mock(); - nativeMethodsMock = new Mock(); - sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object, nativeMethodsMock.Object); + sut = new MouseInterceptorOperation(loggerMock.Object, mouseInterceptorMock.Object); } [TestMethod] @@ -39,7 +36,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { sut.Perform(); - nativeMethodsMock.Verify(n => n.RegisterMouseHook(It.IsAny()), Times.Once); + mouseInterceptorMock.Verify(i => i.Start(), Times.Once); + mouseInterceptorMock.Verify(i => i.Stop(), Times.Never); } [TestMethod] @@ -47,7 +45,8 @@ namespace SafeExamBrowser.Client.UnitTests.Operations { sut.Revert(); - nativeMethodsMock.Verify(n => n.DeregisterMouseHook(It.IsAny()), Times.Once); + mouseInterceptorMock.Verify(i => i.Start(), Times.Never); + mouseInterceptorMock.Verify(i => i.Stop(), Times.Once); } } } diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ProcessMonitorOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ProcessMonitorOperationTests.cs deleted file mode 100644 index c2095667..00000000 --- a/SafeExamBrowser.Client.UnitTests/Operations/ProcessMonitorOperationTests.cs +++ /dev/null @@ -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 logger; - private Mock processMonitor; - private ApplicationSettings settings; - private ProcessMonitorOperation sut; - - [TestInitialize] - public void Initialize() - { - logger = new Mock(); - processMonitor = new Mock(); - 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); - } - } -} diff --git a/SafeExamBrowser.Client.UnitTests/Operations/WindowMonitorOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/WindowMonitorOperationTests.cs deleted file mode 100644 index f92fac30..00000000 --- a/SafeExamBrowser.Client.UnitTests/Operations/WindowMonitorOperationTests.cs +++ /dev/null @@ -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 loggerMock; - private Mock windowMonitorMock; - - [TestInitialize] - public void Initialize() - { - loggerMock = new Mock(); - windowMonitorMock = new Mock(); - } - - [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(); - } - } -} diff --git a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj index ec181aeb..7804c8ff 100644 --- a/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj +++ b/SafeExamBrowser.Client.UnitTests/SafeExamBrowser.Client.UnitTests.csproj @@ -81,6 +81,7 @@ + @@ -88,10 +89,8 @@ - - diff --git a/SafeExamBrowser.Client/ClientContext.cs b/SafeExamBrowser.Client/ClientContext.cs new file mode 100644 index 00000000..f780b278 --- /dev/null +++ b/SafeExamBrowser.Client/ClientContext.cs @@ -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 +{ + /// + /// Holds all configuration and runtime data for the client. + /// + internal class ClientContext + { + /// + /// The global application configuration. + /// + internal AppConfig AppConfig { get; set; } + + /// + /// The settings for the current session. + /// + internal AppSettings Settings { get; set; } + } +} diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index 2d3749b6..2d278072 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -17,14 +17,13 @@ using SafeExamBrowser.Communication.Contracts.Hosts; using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; -using SafeExamBrowser.Settings; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Contracts.Display; -using SafeExamBrowser.Monitoring.Contracts.Processes; -using SafeExamBrowser.Monitoring.Contracts.Windows; +using SafeExamBrowser.Settings; using SafeExamBrowser.UserInterface.Contracts; using SafeExamBrowser.UserInterface.Contracts.MessageBox; using SafeExamBrowser.UserInterface.Contracts.Shell; @@ -36,13 +35,13 @@ namespace SafeExamBrowser.Client internal class ClientController : IClientController { private IActionCenter actionCenter; + private IApplicationMonitor applicationMonitor; private IDisplayMonitor displayMonitor; private IExplorerShell explorerShell; private IHashAlgorithm hashAlgorithm; private ILogger logger; private IMessageBox messageBox; private IOperationSequence operations; - private IProcessMonitor processMonitor; private IRuntimeProxy runtime; private Action shutdown; private ISplashScreen splashScreen; @@ -50,13 +49,12 @@ namespace SafeExamBrowser.Client private ITerminationActivator terminationActivator; private IText text; private IUserInterfaceFactory uiFactory; - private IWindowMonitor windowMonitor; private AppConfig appConfig; public IBrowserApplication Browser { private get; set; } public IClientHost ClientHost { private get; set; } public Guid SessionId { private get; set; } - public ApplicationSettings Settings { private get; set; } + public AppSettings Settings { private get; set; } public AppConfig AppConfig { @@ -73,36 +71,34 @@ namespace SafeExamBrowser.Client public ClientController( IActionCenter actionCenter, + IApplicationMonitor applicationMonitor, IDisplayMonitor displayMonitor, IExplorerShell explorerShell, IHashAlgorithm hashAlgorithm, ILogger logger, IMessageBox messageBox, IOperationSequence operations, - IProcessMonitor processMonitor, IRuntimeProxy runtime, Action shutdown, ITaskbar taskbar, ITerminationActivator terminationActivator, IText text, - IUserInterfaceFactory uiFactory, - IWindowMonitor windowMonitor) + IUserInterfaceFactory uiFactory) { this.actionCenter = actionCenter; + this.applicationMonitor = applicationMonitor; this.displayMonitor = displayMonitor; this.explorerShell = explorerShell; this.hashAlgorithm = hashAlgorithm; this.logger = logger; this.messageBox = messageBox; this.operations = operations; - this.processMonitor = processMonitor; this.runtime = runtime; this.shutdown = shutdown; this.taskbar = taskbar; this.terminationActivator = terminationActivator; this.text = text; this.uiFactory = uiFactory; - this.windowMonitor = windowMonitor; } public bool TryStart() @@ -110,6 +106,7 @@ namespace SafeExamBrowser.Client logger.Info("Initiating startup procedure..."); splashScreen = uiFactory.CreateSplashScreen(); + operations.ActionRequired += Operations_ActionRequired; operations.ProgressChanged += Operations_ProgressChanged; operations.StatusChanged += Operations_StatusChanged; @@ -175,28 +172,26 @@ namespace SafeExamBrowser.Client private void RegisterEvents() { actionCenter.QuitButtonClicked += Shell_QuitButtonClicked; + applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted; Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested; ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested; ClientHost.PasswordRequested += ClientHost_PasswordRequested; ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied; ClientHost.Shutdown += ClientHost_Shutdown; displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged; - processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted; runtime.ConnectionLost += Runtime_ConnectionLost; taskbar.QuitButtonClicked += Shell_QuitButtonClicked; terminationActivator.Activated += TerminationActivator_Activated; - windowMonitor.WindowChanged += WindowMonitor_WindowChanged; } private void DeregisterEvents() { actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked; + applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted; displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged; - processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted; runtime.ConnectionLost -= Runtime_ConnectionLost; taskbar.QuitButtonClicked -= Shell_QuitButtonClicked; terminationActivator.Activated -= TerminationActivator_Activated; - windowMonitor.WindowChanged -= WindowMonitor_WindowChanged; if (Browser != null) { @@ -226,6 +221,18 @@ namespace SafeExamBrowser.Client Browser.Start(); } + private void ApplicationMonitor_ExplorerStarted() + { + logger.Info("Trying to terminate Windows explorer..."); + explorerShell.Terminate(); + logger.Info("Reinitializing working area..."); + displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight()); + logger.Info("Reinitializing shell..."); + actionCenter.InitializeBounds(); + taskbar.InitializeBounds(); + logger.Info("Desktop successfully restored."); + } + private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args) { if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient) @@ -339,6 +346,11 @@ namespace SafeExamBrowser.Client logger.Info("Desktop successfully restored."); } + private void Operations_ActionRequired(ActionRequiredEventArgs args) + { + // TODO + } + private void Operations_ProgressChanged(ProgressChangedEventArgs args) { if (args.CurrentValue.HasValue) @@ -372,18 +384,6 @@ namespace SafeExamBrowser.Client splashScreen?.UpdateStatus(status, true); } - private void ProcessMonitor_ExplorerStarted() - { - logger.Info("Trying to terminate Windows explorer..."); - explorerShell.Terminate(); - logger.Info("Reinitializing working area..."); - displayMonitor.InitializePrimaryDisplay(taskbar.GetAbsoluteHeight()); - logger.Info("Reinitializing shell..."); - actionCenter.InitializeBounds(); - taskbar.InitializeBounds(); - logger.Info("Desktop successfully restored."); - } - private void Runtime_ConnectionLost() { logger.Error("Lost connection to the runtime!"); @@ -406,21 +406,6 @@ namespace SafeExamBrowser.Client terminationActivator.Resume(); } - private void WindowMonitor_WindowChanged(IntPtr window) - { - var allowed = processMonitor.BelongsToAllowedProcess(window); - - if (!allowed) - { - var success = windowMonitor.Hide(window); - - if (!success) - { - windowMonitor.Close(window); - } - } - } - private bool TryInitiateShutdown() { var hasQuitPassword = !String.IsNullOrEmpty(Settings.QuitPasswordHash); diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 0c4298a9..c350287b 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -23,7 +23,6 @@ using SafeExamBrowser.Communication.Contracts.Proxies; using SafeExamBrowser.Communication.Hosts; using SafeExamBrowser.Communication.Proxies; using SafeExamBrowser.Configuration.Contracts; -using SafeExamBrowser.Settings.UserInterface; using SafeExamBrowser.Configuration.Cryptography; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.OperationModel; @@ -32,13 +31,13 @@ using SafeExamBrowser.I18n; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging; using SafeExamBrowser.Logging.Contracts; -using SafeExamBrowser.Monitoring.Contracts.Processes; -using SafeExamBrowser.Monitoring.Contracts.Windows; +using SafeExamBrowser.Monitoring.Applications; +using SafeExamBrowser.Monitoring.Contracts.Applications; using SafeExamBrowser.Monitoring.Display; using SafeExamBrowser.Monitoring.Keyboard; using SafeExamBrowser.Monitoring.Mouse; -using SafeExamBrowser.Monitoring.Processes; -using SafeExamBrowser.Monitoring.Windows; +using SafeExamBrowser.Settings.Logging; +using SafeExamBrowser.Settings.UserInterface; using SafeExamBrowser.SystemComponents; using SafeExamBrowser.SystemComponents.Audio; using SafeExamBrowser.SystemComponents.Contracts; @@ -52,7 +51,6 @@ using SafeExamBrowser.WindowsApi; using SafeExamBrowser.WindowsApi.Contracts; using Desktop = SafeExamBrowser.UserInterface.Desktop; using Mobile = SafeExamBrowser.UserInterface.Mobile; -using SafeExamBrowser.Settings.Logging; namespace SafeExamBrowser.Client { @@ -60,17 +58,18 @@ namespace SafeExamBrowser.Client { private Guid authenticationToken; private ClientConfiguration configuration; + private ClientContext context; private string logFilePath; private LogLevel logLevel; private string runtimeHostUri; private UserInterfaceMode uiMode; private IActionCenter actionCenter; + private IApplicationMonitor applicationMonitor; private IBrowserApplication browser; private IClientHost clientHost; private ILogger logger; private IMessageBox messageBox; - private IProcessMonitor processMonitor; private INativeMethods nativeMethods; private IRuntimeProxy runtimeProxy; private ISystemInfo systemInfo; @@ -79,7 +78,6 @@ namespace SafeExamBrowser.Client private IText text; private ITextResource textResource; private IUserInterfaceFactory uiFactory; - private IWindowMonitor windowMonitor; internal IClientController ClientController { get; private set; } @@ -96,13 +94,13 @@ namespace SafeExamBrowser.Client InitializeText(); actionCenter = BuildActionCenter(); + applicationMonitor = new ApplicationMonitor(new ModuleLogger(logger, nameof(ApplicationMonitor)), nativeMethods); + context = new ClientContext(); messageBox = BuildMessageBox(); - processMonitor = new ProcessMonitor(new ModuleLogger(logger, nameof(ProcessMonitor)), nativeMethods); uiFactory = BuildUserInterfaceFactory(); runtimeProxy = new RuntimeProxy(runtimeHostUri, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(RuntimeProxy)), Interlocutor.Client); taskbar = BuildTaskbar(); terminationActivator = new TerminationActivator(new ModuleLogger(logger, nameof(TerminationActivator))); - windowMonitor = new WindowMonitor(new ModuleLogger(logger, nameof(WindowMonitor)), nativeMethods); var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, nameof(DisplayMonitor)), nativeMethods, systemInfo); var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods); @@ -112,14 +110,13 @@ namespace SafeExamBrowser.Client operations.Enqueue(new I18nOperation(logger, text, textResource)); operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, authenticationToken)); - operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy)); + operations.Enqueue(new ConfigurationOperation(configuration, context, logger, runtimeProxy)); operations.Enqueue(new DelegateOperation(UpdateAppConfig)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostOperation)); operations.Enqueue(new LazyInitializationOperation(BuildClientHostDisconnectionOperation)); operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation)); operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation)); - operations.Enqueue(new LazyInitializationOperation(BuildWindowMonitorOperation)); - operations.Enqueue(new LazyInitializationOperation(BuildProcessMonitorOperation)); + operations.Enqueue(new LazyInitializationOperation(BuildApplicationOperation)); operations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, taskbar)); operations.Enqueue(new LazyInitializationOperation(BuildShellOperation)); operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation)); @@ -130,20 +127,19 @@ namespace SafeExamBrowser.Client ClientController = new ClientController( actionCenter, + applicationMonitor, displayMonitor, explorerShell, hashAlgorithm, logger, messageBox, sequence, - processMonitor, runtimeProxy, shutdown, taskbar, terminationActivator, text, - uiFactory, - windowMonitor); + uiFactory); } internal void LogStartupInformation() @@ -202,6 +198,11 @@ namespace SafeExamBrowser.Client textResource = new XmlTextResource(path); } + private IOperation BuildApplicationOperation() + { + return new ApplicationOperation(applicationMonitor, context, logger); + } + private IOperation BuildBrowserOperation() { var moduleLogger = new ModuleLogger(logger, nameof(BrowserApplication)); @@ -238,25 +239,20 @@ namespace SafeExamBrowser.Client private IOperation BuildKeyboardInterceptorOperation() { - var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor))); - var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger, nativeMethods); + var keyboardInterceptor = new KeyboardInterceptor(configuration.Settings.Keyboard, new ModuleLogger(logger, nameof(KeyboardInterceptor)), nativeMethods); + var operation = new KeyboardInterceptorOperation(keyboardInterceptor, logger); return operation; } private IOperation BuildMouseInterceptorOperation() { - var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse); - var operation = new MouseInterceptorOperation(logger, mouseInterceptor, nativeMethods); + var mouseInterceptor = new MouseInterceptor(new ModuleLogger(logger, nameof(MouseInterceptor)), configuration.Settings.Mouse, nativeMethods); + var operation = new MouseInterceptorOperation(logger, mouseInterceptor); return operation; } - private IOperation BuildProcessMonitorOperation() - { - return new ProcessMonitorOperation(logger, processMonitor, configuration.Settings); - } - private IOperation BuildShellOperation() { var aboutInfo = new AboutNotificationInfo(text); @@ -295,11 +291,6 @@ namespace SafeExamBrowser.Client return operation; } - private IOperation BuildWindowMonitorOperation() - { - return new WindowMonitorOperation(configuration.Settings.KioskMode, logger, windowMonitor); - } - private IActionCenter BuildActionCenter() { switch (uiMode) diff --git a/SafeExamBrowser.Client/Operations/ApplicationOperation.cs b/SafeExamBrowser.Client/Operations/ApplicationOperation.cs new file mode 100644 index 00000000..61afe9c1 --- /dev/null +++ b/SafeExamBrowser.Client/Operations/ApplicationOperation.cs @@ -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 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; + } + } +} diff --git a/SafeExamBrowser.Client/Operations/ClientOperation.cs b/SafeExamBrowser.Client/Operations/ClientOperation.cs new file mode 100644 index 00000000..6a799459 --- /dev/null +++ b/SafeExamBrowser.Client/Operations/ClientOperation.cs @@ -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 +{ + /// + /// The base implementation to be used for all operations in the client operation sequence. + /// + 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(); + } +} diff --git a/SafeExamBrowser.Client/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Client/Operations/ConfigurationOperation.cs index f8534fc9..96120b82 100644 --- a/SafeExamBrowser.Client/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Client/Operations/ConfigurationOperation.cs @@ -15,23 +15,24 @@ using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Client.Operations { - internal class ConfigurationOperation : IOperation + internal class ConfigurationOperation : ClientOperation { private ClientConfiguration configuration; private ILogger logger; private IRuntimeProxy runtime; - public event ActionRequiredEventHandler ActionRequired { add { } remove { } } - public event StatusChangedEventHandler StatusChanged; + public override event ActionRequiredEventHandler ActionRequired { add { } remove { } } + public override event StatusChangedEventHandler StatusChanged; - public ConfigurationOperation(ClientConfiguration configuration, ILogger logger, IRuntimeProxy runtime) + // TODO: Remove and delete ClientConfiguration! + public ConfigurationOperation(ClientConfiguration configuration, ClientContext context, ILogger logger, IRuntimeProxy runtime) : base(context) { this.configuration = configuration; this.logger = logger; this.runtime = runtime; } - public OperationResult Perform() + public override OperationResult Perform() { logger.Info("Initializing application configuration..."); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration); @@ -43,6 +44,9 @@ namespace SafeExamBrowser.Client.Operations configuration.SessionId = config.SessionId; configuration.Settings = config.Settings; + Context.AppConfig = config.AppConfig; + Context.Settings = config.Settings; + logger.Info("Successfully retrieved the application configuration from the runtime."); logger.Info($" -> Client-ID: {configuration.AppConfig.ClientId}"); logger.Info($" -> Runtime-ID: {configuration.AppConfig.RuntimeId}"); @@ -51,7 +55,7 @@ namespace SafeExamBrowser.Client.Operations return OperationResult.Success; } - public OperationResult Revert() + public override OperationResult Revert() { return OperationResult.Success; } diff --git a/SafeExamBrowser.Client/Operations/Events/ProcessTerminationEventArgs.cs b/SafeExamBrowser.Client/Operations/Events/ProcessTerminationEventArgs.cs new file mode 100644 index 00000000..93ef0062 --- /dev/null +++ b/SafeExamBrowser.Client/Operations/Events/ProcessTerminationEventArgs.cs @@ -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; } + } +} diff --git a/SafeExamBrowser.Client/Operations/KeyboardInterceptorOperation.cs b/SafeExamBrowser.Client/Operations/KeyboardInterceptorOperation.cs index c43dddd6..3fb8fd3a 100644 --- a/SafeExamBrowser.Client/Operations/KeyboardInterceptorOperation.cs +++ b/SafeExamBrowser.Client/Operations/KeyboardInterceptorOperation.cs @@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Keyboard; -using SafeExamBrowser.WindowsApi.Contracts; namespace SafeExamBrowser.Client.Operations { @@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations { private IKeyboardInterceptor keyboardInterceptor; private ILogger logger; - private INativeMethods nativeMethods; public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public event StatusChangedEventHandler StatusChanged; - public KeyboardInterceptorOperation( - IKeyboardInterceptor keyboardInterceptor, - ILogger logger, - INativeMethods nativeMethods) + public KeyboardInterceptorOperation(IKeyboardInterceptor keyboardInterceptor, ILogger logger) { this.keyboardInterceptor = keyboardInterceptor; this.logger = logger; - this.nativeMethods = nativeMethods; } public OperationResult Perform() @@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations logger.Info("Starting keyboard interception..."); StatusChanged?.Invoke(TextKey.OperationStatus_StartKeyboardInterception); - nativeMethods.RegisterKeyboardHook(keyboardInterceptor); + keyboardInterceptor.Start(); return OperationResult.Success; } @@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations logger.Info("Stopping keyboard interception..."); StatusChanged?.Invoke(TextKey.OperationStatus_StopKeyboardInterception); - nativeMethods.DeregisterKeyboardHook(keyboardInterceptor); + keyboardInterceptor.Stop(); return OperationResult.Success; } diff --git a/SafeExamBrowser.Client/Operations/MouseInterceptorOperation.cs b/SafeExamBrowser.Client/Operations/MouseInterceptorOperation.cs index 1c352fa0..e77dc2d8 100644 --- a/SafeExamBrowser.Client/Operations/MouseInterceptorOperation.cs +++ b/SafeExamBrowser.Client/Operations/MouseInterceptorOperation.cs @@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Mouse; -using SafeExamBrowser.WindowsApi.Contracts; namespace SafeExamBrowser.Client.Operations { @@ -19,19 +18,14 @@ namespace SafeExamBrowser.Client.Operations { private ILogger logger; private IMouseInterceptor mouseInterceptor; - private INativeMethods nativeMethods; public event ActionRequiredEventHandler ActionRequired { add { } remove { } } public event StatusChangedEventHandler StatusChanged; - public MouseInterceptorOperation( - ILogger logger, - IMouseInterceptor mouseInterceptor, - INativeMethods nativeMethods) + public MouseInterceptorOperation(ILogger logger, IMouseInterceptor mouseInterceptor) { this.logger = logger; this.mouseInterceptor = mouseInterceptor; - this.nativeMethods = nativeMethods; } public OperationResult Perform() @@ -39,7 +33,7 @@ namespace SafeExamBrowser.Client.Operations logger.Info("Starting mouse interception..."); StatusChanged?.Invoke(TextKey.OperationStatus_StartMouseInterception); - nativeMethods.RegisterMouseHook(mouseInterceptor); + mouseInterceptor.Start(); return OperationResult.Success; } @@ -49,7 +43,7 @@ namespace SafeExamBrowser.Client.Operations logger.Info("Stopping mouse interception..."); StatusChanged?.Invoke(TextKey.OperationStatus_StopMouseInterception); - nativeMethods.DeregisterMouseHook(mouseInterceptor); + mouseInterceptor.Stop(); return OperationResult.Success; } diff --git a/SafeExamBrowser.Client/Operations/ProcessMonitorOperation.cs b/SafeExamBrowser.Client/Operations/ProcessMonitorOperation.cs deleted file mode 100644 index 83875b01..00000000 --- a/SafeExamBrowser.Client/Operations/ProcessMonitorOperation.cs +++ /dev/null @@ -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; - } - } -} diff --git a/SafeExamBrowser.Client/Operations/WindowMonitorOperation.cs b/SafeExamBrowser.Client/Operations/WindowMonitorOperation.cs deleted file mode 100644 index 0df4659c..00000000 --- a/SafeExamBrowser.Client/Operations/WindowMonitorOperation.cs +++ /dev/null @@ -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; - } - } -} diff --git a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj index 1f2452f7..52504529 100644 --- a/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj +++ b/SafeExamBrowser.Client/SafeExamBrowser.Client.csproj @@ -71,9 +71,12 @@ + + + @@ -88,9 +91,8 @@ - + - Code @@ -220,6 +222,7 @@ + robocopy "$(SolutionDir)SafeExamBrowser.Browser\bin\$(PlatformName)\$(ConfigurationName)" "$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)" /e /np diff --git a/SafeExamBrowser.Configuration.Contracts/ClientConfiguration.cs b/SafeExamBrowser.Configuration.Contracts/ClientConfiguration.cs index b51ab020..139fb714 100644 --- a/SafeExamBrowser.Configuration.Contracts/ClientConfiguration.cs +++ b/SafeExamBrowser.Configuration.Contracts/ClientConfiguration.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Configuration.Contracts { @@ -29,6 +30,6 @@ namespace SafeExamBrowser.Configuration.Contracts /// /// The application settings to be used by the client. /// - public Settings.ApplicationSettings Settings { get; set; } + public AppSettings Settings { get; set; } } } diff --git a/SafeExamBrowser.Configuration.Contracts/IConfigurationRepository.cs b/SafeExamBrowser.Configuration.Contracts/IConfigurationRepository.cs index 3e277f19..9e6448fd 100644 --- a/SafeExamBrowser.Configuration.Contracts/IConfigurationRepository.cs +++ b/SafeExamBrowser.Configuration.Contracts/IConfigurationRepository.cs @@ -10,6 +10,7 @@ using System; using SafeExamBrowser.Configuration.Contracts.Cryptography; using SafeExamBrowser.Configuration.Contracts.DataFormats; using SafeExamBrowser.Configuration.Contracts.DataResources; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Configuration.Contracts { @@ -36,7 +37,7 @@ namespace SafeExamBrowser.Configuration.Contracts /// /// Loads the default settings. /// - Settings.ApplicationSettings LoadDefaultSettings(); + AppSettings LoadDefaultSettings(); /// /// Registers the specified to be used to parse configuration data. @@ -61,6 +62,6 @@ namespace SafeExamBrowser.Configuration.Contracts /// /// Attempts to load settings from the specified resource. /// - LoadStatus TryLoadSettings(Uri resource, out Settings.ApplicationSettings settings, PasswordParameters password = null); + LoadStatus TryLoadSettings(Uri resource, out AppSettings settings, PasswordParameters password = null); } } diff --git a/SafeExamBrowser.Configuration.Contracts/ServiceConfiguration.cs b/SafeExamBrowser.Configuration.Contracts/ServiceConfiguration.cs index c428327a..2714e0c6 100644 --- a/SafeExamBrowser.Configuration.Contracts/ServiceConfiguration.cs +++ b/SafeExamBrowser.Configuration.Contracts/ServiceConfiguration.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Configuration.Contracts { @@ -29,7 +30,7 @@ namespace SafeExamBrowser.Configuration.Contracts /// /// The application settings to be used by the service. /// - public Settings.ApplicationSettings Settings { get; set; } + public AppSettings Settings { get; set; } /// /// The user name of the currently logged in user. diff --git a/SafeExamBrowser.Configuration.Contracts/SessionConfiguration.cs b/SafeExamBrowser.Configuration.Contracts/SessionConfiguration.cs index efc1ac3a..9dbb5314 100644 --- a/SafeExamBrowser.Configuration.Contracts/SessionConfiguration.cs +++ b/SafeExamBrowser.Configuration.Contracts/SessionConfiguration.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Settings; namespace SafeExamBrowser.Configuration.Contracts { @@ -16,7 +17,7 @@ namespace SafeExamBrowser.Configuration.Contracts public class SessionConfiguration { /// - /// The active application configuration for this session. + /// The application configuration for this session. /// public AppConfig AppConfig { get; set; } @@ -33,6 +34,6 @@ namespace SafeExamBrowser.Configuration.Contracts /// /// The settings used for this session. /// - public Settings.ApplicationSettings Settings { get; set; } + public AppSettings Settings { get; set; } } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs new file mode 100644 index 00000000..0c45fd62 --- /dev/null +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Applications.cs @@ -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 applications) + { + foreach (var item in applications) + { + if (item is IDictionary 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 applications) + { + foreach (var item in applications) + { + if (item is IDictionary 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) + { + + } + } + } + } + } + } +} diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Audio.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Audio.cs index 75d8cfb5..2bebbc5d 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Audio.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Audio.cs @@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapInitialVolumeLevel(ApplicationSettings settings, object value) + private void MapInitialVolumeLevel(AppSettings settings, object value) { if (value is int volume) { @@ -20,7 +20,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapMuteAudio(ApplicationSettings settings, object value) + private void MapMuteAudio(AppSettings settings, object value) { if (value is bool mute) { @@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapSetInitialVolumeLevel(ApplicationSettings settings, object value) + private void MapSetInitialVolumeLevel(AppSettings settings, object value) { if (value is bool initialize) { diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs index bfccfcda..db2f838c 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Browser.cs @@ -15,7 +15,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapAllowAddressBar(ApplicationSettings settings, object value) + private void MapAllowAddressBar(AppSettings settings, object value) { if (value is bool allow) { @@ -23,7 +23,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowAddressBarAdditionalWindow(ApplicationSettings settings, object value) + private void MapAllowAddressBarAdditionalWindow(AppSettings settings, object value) { if (value is bool allow) { @@ -31,7 +31,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowConfigurationDownloads(ApplicationSettings settings, object value) + private void MapAllowConfigurationDownloads(AppSettings settings, object value) { if (value is bool allow) { @@ -39,7 +39,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowDeveloperConsole(ApplicationSettings settings, object value) + private void MapAllowDeveloperConsole(AppSettings settings, object value) { if (value is bool allow) { @@ -48,7 +48,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowDownloads(ApplicationSettings settings, object value) + private void MapAllowDownloads(AppSettings settings, object value) { if (value is bool allow) { @@ -56,7 +56,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowNavigation(ApplicationSettings settings, object value) + private void MapAllowNavigation(AppSettings settings, object value) { if (value is bool allow) { @@ -65,7 +65,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowNavigationAdditionalWindow(ApplicationSettings settings, object value) + private void MapAllowNavigationAdditionalWindow(AppSettings settings, object value) { if (value is bool allow) { @@ -74,7 +74,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowPageZoom(ApplicationSettings settings, object value) + private void MapAllowPageZoom(AppSettings settings, object value) { if (value is bool allow) { @@ -82,7 +82,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowPopups(ApplicationSettings settings, object value) + private void MapAllowPopups(AppSettings settings, object value) { if (value is bool block) { @@ -90,7 +90,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowReload(ApplicationSettings settings, object value) + private void MapAllowReload(AppSettings settings, object value) { if (value is bool allow) { @@ -98,7 +98,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAllowReloadAdditionalWindow(ApplicationSettings settings, object value) + private void MapAllowReloadAdditionalWindow(AppSettings settings, object value) { if (value is bool allow) { @@ -106,7 +106,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableContentRequestFilter(ApplicationSettings settings, object value) + private void MapEnableContentRequestFilter(AppSettings settings, object value) { if (value is bool process) { @@ -114,7 +114,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableMainRequestFilter(ApplicationSettings settings, object value) + private void MapEnableMainRequestFilter(AppSettings settings, object value) { if (value is bool process) { @@ -122,7 +122,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapUrlFilterRules(ApplicationSettings settings, object value) + private void MapUrlFilterRules(AppSettings settings, object value) { const int ALLOW = 1; @@ -132,7 +132,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { if (item is IDictionary ruleData) { - if (ruleData.TryGetValue(Keys.Browser.Filter.RuleIsActive, out var v) && v is bool active && active) + var isActive = ruleData.TryGetValue(Keys.Browser.Filter.RuleIsActive, out var v) && v is bool active && active; + + if (isActive) { var rule = new FilterRuleSettings(); @@ -158,7 +160,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapMainWindowMode(ApplicationSettings settings, object value) + private void MapMainWindowMode(AppSettings settings, object value) { const int FULLSCREEN = 1; @@ -168,7 +170,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapShowReloadWarning(ApplicationSettings settings, object value) + private void MapShowReloadWarning(AppSettings settings, object value) { if (value is bool show) { @@ -176,7 +178,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapShowReloadWarningAdditionalWindow(ApplicationSettings settings, object value) + private void MapShowReloadWarningAdditionalWindow(AppSettings settings, object value) { if (value is bool show) { @@ -184,7 +186,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapUserAgentMode(IDictionary rawData, ApplicationSettings settings) + private void MapUserAgentMode(IDictionary rawData, AppSettings settings) { const int DEFAULT = 0; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.ConfigurationFile.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.ConfigurationFile.cs index 623b61ea..f758051b 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.ConfigurationFile.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.ConfigurationFile.cs @@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapConfigurationMode(ApplicationSettings settings, object value) + private void MapConfigurationMode(AppSettings settings, object value) { const int CONFIGURE_CLIENT = 1; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.General.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.General.cs index 938af9ef..6a3a088f 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.General.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.General.cs @@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapAdminPasswordHash(ApplicationSettings settings, object value) + private void MapAdminPasswordHash(AppSettings settings, object value) { if (value is string hash) { @@ -22,7 +22,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapApplicationLogAccess(IDictionary rawData, ApplicationSettings settings) + private void MapApplicationLogAccess(IDictionary rawData, AppSettings settings) { var hasValue = rawData.TryGetValue(Keys.General.AllowApplicationLog, out var value); @@ -42,7 +42,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapLogLevel(ApplicationSettings settings, object value) + private void MapLogLevel(AppSettings settings, object value) { const int ERROR = 0, WARNING = 1, INFO = 2; @@ -52,7 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapQuitPasswordHash(ApplicationSettings settings, object value) + private void MapQuitPasswordHash(AppSettings settings, object value) { if (value is string hash) { @@ -60,7 +60,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapStartUrl(ApplicationSettings settings, object value) + private void MapStartUrl(AppSettings settings, object value) { if (value is string url) { diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Input.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Input.cs index 40c0d124..7b34dfe2 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Input.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Input.cs @@ -12,7 +12,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapEnableAltEsc(ApplicationSettings settings, object value) + private void MapEnableAltEsc(AppSettings settings, object value) { if (value is bool enabled) { @@ -20,7 +20,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableAltF4(ApplicationSettings settings, object value) + private void MapEnableAltF4(AppSettings settings, object value) { if (value is bool enabled) { @@ -28,7 +28,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableAltTab(ApplicationSettings settings, object value) + private void MapEnableAltTab(AppSettings settings, object value) { if (value is bool enabled) { @@ -36,7 +36,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableCtrlEsc(ApplicationSettings settings, object value) + private void MapEnableCtrlEsc(AppSettings settings, object value) { if (value is bool enabled) { @@ -44,7 +44,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableEsc(ApplicationSettings settings, object value) + private void MapEnableEsc(AppSettings settings, object value) { if (value is bool enabled) { @@ -52,7 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF1(ApplicationSettings settings, object value) + private void MapEnableF1(AppSettings settings, object value) { if (value is bool enabled) { @@ -60,7 +60,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF2(ApplicationSettings settings, object value) + private void MapEnableF2(AppSettings settings, object value) { if (value is bool enabled) { @@ -68,7 +68,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF3(ApplicationSettings settings, object value) + private void MapEnableF3(AppSettings settings, object value) { if (value is bool enabled) { @@ -76,7 +76,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF4(ApplicationSettings settings, object value) + private void MapEnableF4(AppSettings settings, object value) { if (value is bool enabled) { @@ -84,7 +84,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF5(ApplicationSettings settings, object value) + private void MapEnableF5(AppSettings settings, object value) { if (value is bool enabled) { @@ -92,7 +92,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF6(ApplicationSettings settings, object value) + private void MapEnableF6(AppSettings settings, object value) { if (value is bool enabled) { @@ -100,7 +100,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF7(ApplicationSettings settings, object value) + private void MapEnableF7(AppSettings settings, object value) { if (value is bool enabled) { @@ -108,7 +108,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF8(ApplicationSettings settings, object value) + private void MapEnableF8(AppSettings settings, object value) { if (value is bool enabled) { @@ -116,7 +116,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF9(ApplicationSettings settings, object value) + private void MapEnableF9(AppSettings settings, object value) { if (value is bool enabled) { @@ -124,7 +124,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF10(ApplicationSettings settings, object value) + private void MapEnableF10(AppSettings settings, object value) { if (value is bool enabled) { @@ -132,7 +132,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF11(ApplicationSettings settings, object value) + private void MapEnableF11(AppSettings settings, object value) { if (value is bool enabled) { @@ -140,7 +140,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableF12(ApplicationSettings settings, object value) + private void MapEnableF12(AppSettings settings, object value) { if (value is bool enabled) { @@ -148,7 +148,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnablePrintScreen(ApplicationSettings settings, object value) + private void MapEnablePrintScreen(AppSettings settings, object value) { if (value is bool enabled) { @@ -156,7 +156,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableSystemKey(ApplicationSettings settings, object value) + private void MapEnableSystemKey(AppSettings settings, object value) { if (value is bool enabled) { @@ -164,7 +164,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapEnableRightMouse(ApplicationSettings settings, object value) + private void MapEnableRightMouse(AppSettings settings, object value) { if (value is bool enabled) { diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Security.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Security.cs index 73a9a4f3..e27b7dd4 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Security.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.Security.cs @@ -14,7 +14,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapKioskMode(IDictionary rawData, ApplicationSettings settings) + private void MapKioskMode(IDictionary rawData, AppSettings settings) { var hasCreateNewDesktop = rawData.TryGetValue(Keys.Security.KioskModeCreateNewDesktop, out var createNewDesktop); var hasDisableExplorerShell = rawData.TryGetValue(Keys.Security.KioskModeDisableExplorerShell, out var disableExplorerShell); @@ -35,7 +35,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapServicePolicy(ApplicationSettings settings, object value) + private void MapServicePolicy(AppSettings settings, object value) { const int WARN = 1; const int FORCE = 2; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs index 2d0c5f8f..06b3741f 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.UserInterface.cs @@ -13,7 +13,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - private void MapApplicationLogButton(ApplicationSettings settings, object value) + private void MapApplicationLogButton(AppSettings settings, object value) { if (value is bool show) { @@ -21,7 +21,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapAudio(ApplicationSettings settings, object value) + private void MapAudio(AppSettings settings, object value) { if (value is bool show) { @@ -30,7 +30,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapClock(ApplicationSettings settings, object value) + private void MapClock(AppSettings settings, object value) { if (value is bool show) { @@ -39,7 +39,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapKeyboardLayout(ApplicationSettings settings, object value) + private void MapKeyboardLayout(AppSettings settings, object value) { if (value is bool show) { @@ -48,7 +48,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapWirelessNetwork(ApplicationSettings settings, object value) + private void MapWirelessNetwork(AppSettings settings, object value) { if (value is bool show) { @@ -57,7 +57,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapUserInterfaceMode(ApplicationSettings settings, object value) + private void MapUserInterfaceMode(AppSettings settings, object value) { if (value is bool mobile) { diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs index 6c23f198..11bdb604 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapper.cs @@ -13,10 +13,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal partial class DataMapper { - internal void MapRawDataToSettings(IDictionary rawData, ApplicationSettings settings) + internal void MapRawDataToSettings(IDictionary rawData, AppSettings settings) { foreach (var item in rawData) { + MapApplicationSettings(item.Key, item.Value, settings); MapAudioSettings(item.Key, item.Value, settings); MapBrowserSettings(item.Key, item.Value, settings); MapConfigurationFileSettings(item.Key, item.Value, settings); @@ -31,7 +32,20 @@ namespace SafeExamBrowser.Configuration.ConfigurationData MapUserAgentMode(rawData, settings); } - private void MapAudioSettings(string key, object value, ApplicationSettings settings) + private void MapApplicationSettings(string key, object value, AppSettings settings) + { + switch (key) + { + case Keys.Applications.Blacklist: + MapApplicationBlacklist(settings, value); + break; + case Keys.Applications.Whitelist: + MapApplicationWhitelist(settings, value); + break; + } + } + + private void MapAudioSettings(string key, object value, AppSettings settings) { switch (key) { @@ -47,7 +61,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapBrowserSettings(string key, object value, ApplicationSettings settings) + private void MapBrowserSettings(string key, object value, AppSettings settings) { switch (key) { @@ -105,7 +119,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapConfigurationFileSettings(string key, object value, ApplicationSettings settings) + private void MapConfigurationFileSettings(string key, object value, AppSettings settings) { switch (key) { @@ -115,7 +129,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapGeneralSettings(string key, object value, ApplicationSettings settings) + private void MapGeneralSettings(string key, object value, AppSettings settings) { switch (key) { @@ -134,7 +148,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapInputSettings(string key, object value, ApplicationSettings settings) + private void MapInputSettings(string key, object value, AppSettings settings) { switch (key) { @@ -201,7 +215,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapSecuritySettings(string key, object value, ApplicationSettings settings) + private void MapSecuritySettings(string key, object value, AppSettings settings) { switch (key) { @@ -211,7 +225,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData } } - private void MapUserInterfaceSettings(string key, object value, ApplicationSettings settings) + private void MapUserInterfaceSettings(string key, object value, AppSettings settings) { switch (key) { diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index 202e8b10..fea73493 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -99,9 +99,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData return configuration; } - internal ApplicationSettings LoadDefaultSettings() + internal AppSettings LoadDefaultSettings() { - var settings = new ApplicationSettings(); + var settings = new AppSettings(); settings.ActionCenter.EnableActionCenter = true; settings.ActionCenter.ShowApplicationInfo = true; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs index 67e8b6d5..f114624b 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs @@ -10,12 +10,21 @@ namespace SafeExamBrowser.Configuration.ConfigurationData { internal static class Keys { + internal const int WINDOWS = 1; + internal static class AdditionalResources { } internal static class Applications { + internal const string ApplicationActive = "active"; + internal const string ApplicationAutoTerminate = "strongKill"; + internal const string ApplicationExecutable = "executable"; + internal const string ApplicationOriginalName = "originalName"; + internal const string ApplicationOs = "os"; + internal const string Blacklist = "prohibitedProcesses"; + internal const string Whitelist = "permittedProcesses"; } internal static class Audio diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs index 80a88a5d..5ca013cb 100644 --- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs +++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs @@ -100,7 +100,7 @@ namespace SafeExamBrowser.Configuration return dataValues.InitializeSessionConfiguration(); } - public ApplicationSettings LoadDefaultSettings() + public AppSettings LoadDefaultSettings() { return dataValues.LoadDefaultSettings(); } @@ -125,7 +125,7 @@ namespace SafeExamBrowser.Configuration resourceSavers.Add(saver); } - public LoadStatus TryLoadSettings(Uri resource, out ApplicationSettings settings, PasswordParameters password = null) + public LoadStatus TryLoadSettings(Uri resource, out AppSettings settings, PasswordParameters password = null) { logger.Info($"Attempting to load '{resource}'..."); diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index dfc50fe2..58dd3f52 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -66,6 +66,9 @@ + + DataMapper.cs + DataMapper.cs diff --git a/SafeExamBrowser.Monitoring.Contracts/Processes/Events/ExplorerStartedEventHandler.cs b/SafeExamBrowser.Monitoring.Contracts/Applications/Events/ExplorerStartedEventHandler.cs similarity index 86% rename from SafeExamBrowser.Monitoring.Contracts/Processes/Events/ExplorerStartedEventHandler.cs rename to SafeExamBrowser.Monitoring.Contracts/Applications/Events/ExplorerStartedEventHandler.cs index 75e25062..fea16098 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Processes/Events/ExplorerStartedEventHandler.cs +++ b/SafeExamBrowser.Monitoring.Contracts/Applications/Events/ExplorerStartedEventHandler.cs @@ -6,7 +6,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Monitoring.Contracts.Processes.Events +namespace SafeExamBrowser.Monitoring.Contracts.Applications.Events { /// /// Indicates that the Windows explorer process has started. diff --git a/SafeExamBrowser.Monitoring.Contracts/Applications/IApplicationMonitor.cs b/SafeExamBrowser.Monitoring.Contracts/Applications/IApplicationMonitor.cs new file mode 100644 index 00000000..1c163649 --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/Applications/IApplicationMonitor.cs @@ -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 +{ + /// + /// Monitors applications running on the computer. + /// + public interface IApplicationMonitor + { + /// + /// Event fired when a new instance of the Windows Explorer has been started. + /// + event ExplorerStartedEventHandler ExplorerStarted; + + /// + /// Initializes the application monitor. + /// + InitializationResult Initialize(ApplicationSettings settings); + + /// + /// Starts monitoring all initialized applications. Windows Explorer will always be monitored. + /// + void Start(); + + /// + /// Stops the application monitoring. + /// + void Stop(); + } +} diff --git a/SafeExamBrowser.Monitoring.Contracts/Applications/InitializationResult.cs b/SafeExamBrowser.Monitoring.Contracts/Applications/InitializationResult.cs new file mode 100644 index 00000000..95121322 --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/Applications/InitializationResult.cs @@ -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 +{ + /// + /// Provides information about the initialization of the . + /// + public class InitializationResult + { + /// + /// A list of currently running applications which need to be terminated. + /// + public IEnumerable RunningApplications { get; } + + public InitializationResult() + { + RunningApplications = new List(); + } + } +} diff --git a/SafeExamBrowser.Monitoring.Contracts/Applications/RunningApplicationInfo.cs b/SafeExamBrowser.Monitoring.Contracts/Applications/RunningApplicationInfo.cs new file mode 100644 index 00000000..2fe38210 --- /dev/null +++ b/SafeExamBrowser.Monitoring.Contracts/Applications/RunningApplicationInfo.cs @@ -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 +{ + /// + /// Provides information about a running application. + /// + public class RunningApplicationInfo + { + /// + /// The name of the application. + /// + public string Name { get; set; } + + /// + /// A list of processes which belong to the application. + /// + public IEnumerable Processes { get; set; } + } +} diff --git a/SafeExamBrowser.Monitoring.Contracts/Keyboard/IKeyboardInterceptor.cs b/SafeExamBrowser.Monitoring.Contracts/Keyboard/IKeyboardInterceptor.cs index 2f40ffc6..009f1dbc 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Keyboard/IKeyboardInterceptor.cs +++ b/SafeExamBrowser.Monitoring.Contracts/Keyboard/IKeyboardInterceptor.cs @@ -14,8 +14,13 @@ namespace SafeExamBrowser.Monitoring.Contracts.Keyboard public interface IKeyboardInterceptor { /// - /// Returns true if the given key should be blocked, otherwise false. The key code corresponds to a Win32 Virtual-Key. + /// Starts intercepting keyboard input. /// - bool Block(int keyCode, KeyModifier modifier, KeyState state); + void Start(); + + /// + /// Stops intercepting keyboard input. + /// + void Stop(); } } diff --git a/SafeExamBrowser.Monitoring.Contracts/Mouse/IMouseInterceptor.cs b/SafeExamBrowser.Monitoring.Contracts/Mouse/IMouseInterceptor.cs index 8df646bf..c9741551 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Mouse/IMouseInterceptor.cs +++ b/SafeExamBrowser.Monitoring.Contracts/Mouse/IMouseInterceptor.cs @@ -14,8 +14,13 @@ namespace SafeExamBrowser.Monitoring.Contracts.Mouse public interface IMouseInterceptor { /// - /// Returns true if the given button should be blocked, otherwise false. + /// Starts intercepting mouse input. /// - bool Block(MouseButton button, MouseButtonState state); + void Start(); + + /// + /// Stops intercepting mouse input. + /// + void Stop(); } } diff --git a/SafeExamBrowser.Monitoring.Contracts/Processes/IProcessMonitor.cs b/SafeExamBrowser.Monitoring.Contracts/Processes/IProcessMonitor.cs deleted file mode 100644 index edfaecb1..00000000 --- a/SafeExamBrowser.Monitoring.Contracts/Processes/IProcessMonitor.cs +++ /dev/null @@ -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 -{ - /// - /// Monitors the processes running on the computer and provides access to process-related functionality. - /// - public interface IProcessMonitor - { - /// - /// Event fired when the process monitor observes that a new instance of the Windows explorer has been started. - /// - event ExplorerStartedEventHandler ExplorerStarted; - - /// - /// Performs a check whether the process associated to the given window is allowed. - /// - bool BelongsToAllowedProcess(IntPtr window); - - /// - /// Starts monitoring the Windows explorer, i.e. any newly created instances of explorer.exe will trigger the - /// event. - /// - void StartMonitoringExplorer(); - - /// - /// Stops monitoring the Windows explorer. - /// - void StopMonitoringExplorer(); - } -} diff --git a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj index 050dac20..1b827236 100644 --- a/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj +++ b/SafeExamBrowser.Monitoring.Contracts/SafeExamBrowser.Monitoring.Contracts.csproj @@ -53,19 +53,25 @@ + - - + - - - - - - + + + + + {30b2d907-5861-4f39-abad-c4abf1b3470e} + SafeExamBrowser.Settings + + + {7016f080-9aa5-41b2-a225-385ad877c171} + SafeExamBrowser.WindowsApi.Contracts + + \ No newline at end of file diff --git a/SafeExamBrowser.Monitoring.Contracts/Windows/IWindowMonitor.cs b/SafeExamBrowser.Monitoring.Contracts/Windows/IWindowMonitor.cs deleted file mode 100644 index 4b09ff44..00000000 --- a/SafeExamBrowser.Monitoring.Contracts/Windows/IWindowMonitor.cs +++ /dev/null @@ -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 -{ - /// - /// Monitors the windows associated with the current desktop and provides window-related functionality. - /// - public interface IWindowMonitor - { - /// - /// Event fired when the window monitor observes that the foreground window has changed. - /// - event WindowChangedEventHandler WindowChanged; - - /// - /// Forcefully closes the specified window. - /// - void Close(IntPtr window); - - /// - /// Hides the specified window. Returns true if the window was successfully hidden, otherwise false. - /// - bool Hide(IntPtr window); - - /// - /// 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. - /// - void StartMonitoringWindows(); - - /// - /// Stops monitoring windows and deregisters from any subscribed system events. - /// - void StopMonitoringWindows(); - } -} diff --git a/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs b/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs new file mode 100644 index 00000000..4a524388 --- /dev/null +++ b/SafeExamBrowser.Monitoring/Applications/ApplicationMonitor.cs @@ -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}'"; + } + } +} diff --git a/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs b/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs index 8d983ba3..86b3b19c 100644 --- a/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs +++ b/SafeExamBrowser.Monitoring/Keyboard/KeyboardInterceptor.cs @@ -9,24 +9,42 @@ using System; using System.Linq; using System.Windows.Input; -using SafeExamBrowser.Settings.Monitoring; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Keyboard; +using SafeExamBrowser.Settings.Monitoring; +using SafeExamBrowser.WindowsApi.Contracts; +using SafeExamBrowser.WindowsApi.Contracts.Events; namespace SafeExamBrowser.Monitoring.Keyboard { public class KeyboardInterceptor : IKeyboardInterceptor { - private KeyboardSettings settings; + private Guid? hookId; private ILogger logger; + private INativeMethods nativeMethods; + private KeyboardSettings settings; - public KeyboardInterceptor(KeyboardSettings settings, ILogger logger) + public KeyboardInterceptor(KeyboardSettings settings, ILogger logger, INativeMethods nativeMethods) { this.logger = logger; + this.nativeMethods = nativeMethods; this.settings = settings; } - public bool Block(int keyCode, KeyModifier modifier, KeyState state) + public void Start() + { + hookId = nativeMethods.RegisterKeyboardHook(KeyboardHookCallback); + } + + public void Stop() + { + if (hookId.HasValue) + { + nativeMethods.DeregisterKeyboardHook(hookId.Value); + } + } + + private bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state) { var block = false; var key = KeyInterop.KeyFromVirtualKey(keyCode); diff --git a/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs b/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs index 17f160bf..99bd65d9 100644 --- a/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs +++ b/SafeExamBrowser.Monitoring/Mouse/MouseInterceptor.cs @@ -6,24 +6,43 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using SafeExamBrowser.Settings.Monitoring; +using System; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Monitoring.Contracts.Mouse; +using SafeExamBrowser.Settings.Monitoring; +using SafeExamBrowser.WindowsApi.Contracts; +using SafeExamBrowser.WindowsApi.Contracts.Events; namespace SafeExamBrowser.Monitoring.Mouse { public class MouseInterceptor : IMouseInterceptor { + private Guid? hookId; private ILogger logger; + private INativeMethods nativeMethods; private MouseSettings settings; - public MouseInterceptor(ILogger logger, MouseSettings settings) + public MouseInterceptor(ILogger logger, MouseSettings settings, INativeMethods nativeMethods) { this.logger = logger; + this.nativeMethods = nativeMethods; this.settings = settings; } - public bool Block(MouseButton button, MouseButtonState state) + public void Start() + { + hookId = nativeMethods.RegisterMouseHook(MouseHookCallback); + } + + public void Stop() + { + if (hookId.HasValue) + { + nativeMethods.DeregisterMouseHook(hookId.Value); + } + } + + private bool MouseHookCallback(MouseButton button, MouseButtonState state) { var block = false; diff --git a/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs b/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs deleted file mode 100644 index 4d4f175c..00000000 --- a/SafeExamBrowser.Monitoring/Processes/ProcessMonitor.cs +++ /dev/null @@ -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}'"; - } - } -} diff --git a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj index 162bacaa..01f4c292 100644 --- a/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj +++ b/SafeExamBrowser.Monitoring/SafeExamBrowser.Monitoring.csproj @@ -60,9 +60,8 @@ - + - diff --git a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs b/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs deleted file mode 100644 index 90bc26cb..00000000 --- a/SafeExamBrowser.Monitoring/Windows/WindowMonitor.cs +++ /dev/null @@ -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); - } - } - } -} diff --git a/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs index dcfd6878..b9d2a239 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Communication/RuntimeHostTests.cs @@ -171,7 +171,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Communication public void MustHandleConfigurationRequestCorrectly() { var args = default(ClientConfigurationEventArgs); - var configuration = new ClientConfiguration { Settings = new ApplicationSettings { AdminPasswordHash = "12345" } }; + var configuration = new ClientConfiguration { Settings = new AppSettings { AdminPasswordHash = "12345" } }; sut.AllowConnection = true; sut.ClientConfigurationNeeded += (a) => { args = a; args.ClientConfiguration = configuration; }; diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ClientOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ClientOperationTests.cs index a43d5e47..7d9dc8d6 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/ClientOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ClientOperationTests.cs @@ -37,7 +37,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations private Mock runtimeHost; private SessionConfiguration session; private SessionContext sessionContext; - private ApplicationSettings settings; + private AppSettings settings; private ClientOperation sut; [TestInitialize] @@ -53,7 +53,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations runtimeHost = new Mock(); session = new SessionConfiguration(); sessionContext = new SessionContext(); - settings = new ApplicationSettings(); + settings = new AppSettings(); terminated = new Action(() => { runtimeHost.Raise(h => h.ClientDisconnected += null); diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs index 7ea63312..46ec7ac2 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs @@ -56,7 +56,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustUseCommandLineArgumentAs1stPrio() { - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam }; var url = @"http://www.safeexambrowser.org/whatever.seb"; var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); @@ -77,7 +77,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustUseProgramDataAs2ndPrio() { var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); - var settings = default(ApplicationSettings); + var settings = default(AppSettings); appConfig.ProgramDataFilePath = location; @@ -94,7 +94,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustUseAppDataAs3rdPrio() { var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); - var settings = default(ApplicationSettings); + var settings = default(AppSettings); appConfig.AppDataFilePath = location; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); @@ -109,7 +109,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustTestdatalyHandleBrowserResource() { - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam }; var url = @"http://www.safeexambrowser.org/whatever.seb"; nextSession.Settings = settings; @@ -125,7 +125,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustFallbackToDefaultsAsLastPrio() { - var defaultSettings = new ApplicationSettings(); + var defaultSettings = new AppSettings(); repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); @@ -141,7 +141,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustAbortIfWishedByUser() { - var settings = new ApplicationSettings(); + var settings = new AppSettings(); var url = @"http://www.safeexambrowser.org/whatever.seb"; sessionContext.Current = null; @@ -166,7 +166,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustNotAbortIfNotWishedByUser() { - var settings = new ApplicationSettings(); + var settings = new AppSettings(); var url = @"http://www.safeexambrowser.org/whatever.seb"; settings.ConfigurationMode = ConfigurationMode.ConfigureClient; @@ -191,7 +191,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustInformAboutClientConfigurationError() { var informed = false; - var settings = new ApplicationSettings(); + var settings = new AppSettings(); var url = @"http://www.safeexambrowser.org/whatever.seb"; settings.ConfigurationMode = ConfigurationMode.ConfigureClient; @@ -216,7 +216,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustNotAllowToAbortIfNotInConfigureClientMode() { - var settings = new ApplicationSettings(); + var settings = new AppSettings(); settings.ConfigurationMode = ConfigurationMode.Exam; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); @@ -238,7 +238,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustNotFailWithoutCommandLineArgs() { - var defaultSettings = new ApplicationSettings(); + var defaultSettings = new AppSettings(); var result = OperationResult.Failed; repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); @@ -272,8 +272,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustOnlyAllowToEnterAdminPasswordFiveTimes() { var count = 0; - var localSettings = new ApplicationSettings { AdminPasswordHash = "1234" }; - var settings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var localSettings = new AppSettings { AdminPasswordHash = "1234" }; + var settings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; var url = @"http://www.safeexambrowser.org/whatever.seb"; appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); @@ -302,7 +302,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustOnlyAllowToEnterSettingsPasswordFiveTimes() { var count = 0; - var settings = default(ApplicationSettings); + var settings = default(AppSettings); var url = @"http://www.safeexambrowser.org/whatever.seb"; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); @@ -329,8 +329,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustSucceedIfAdminPasswordTestdata() { var password = "test"; - var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; - var nextSettings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var nextSettings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; var url = @"http://www.safeexambrowser.org/whatever.seb"; nextSession.Settings = nextSettings; @@ -359,8 +359,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustNotAuthenticateIfSameAdminPassword() { - var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; - var nextSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var nextSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; var url = @"http://www.safeexambrowser.org/whatever.seb"; nextSession.Settings = nextSettings; @@ -388,7 +388,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustSucceedIfSettingsPasswordTestdata() { var password = "test"; - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam }; var url = @"http://www.safeexambrowser.org/whatever.seb"; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); @@ -416,7 +416,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations { var url = @"http://www.safeexambrowser.org/whatever.seb"; var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); - var settings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.Exam }; + var settings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.Exam }; appConfig.AppDataFilePath = location; @@ -442,8 +442,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustAbortAskingForAdminPasswordIfDecidedByUser() { var password = "test"; - var currentSettings = new ApplicationSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; - var nextSettings = new ApplicationSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var currentSettings = new AppSettings { AdminPasswordHash = "1234", ConfigurationMode = ConfigurationMode.ConfigureClient }; + var nextSettings = new AppSettings { AdminPasswordHash = "9876", ConfigurationMode = ConfigurationMode.ConfigureClient }; var url = @"http://www.safeexambrowser.org/whatever.seb"; appConfig.AppDataFilePath = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations), "Testdata", FILE_NAME); @@ -472,7 +472,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Perform_MustAbortAskingForSettingsPasswordIfDecidedByUser() { - var settings = default(ApplicationSettings); + var settings = default(AppSettings); var url = @"http://www.safeexambrowser.org/whatever.seb"; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); @@ -494,10 +494,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Repeat_MustPerformForExamWithTestdataUri() { - var currentSettings = new ApplicationSettings(); + var currentSettings = new AppSettings(); var location = Path.GetDirectoryName(GetType().Assembly.Location); var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME)); - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.Exam }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.Exam }; currentSession.Settings = currentSettings; sessionContext.ReconfigurationFilePath = resource.LocalPath; @@ -515,10 +515,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Repeat_MustPerformForClientConfigurationWithTestdataUri() { - var currentSettings = new ApplicationSettings(); + var currentSettings = new AppSettings(); var location = Path.GetDirectoryName(GetType().Assembly.Location); var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME)); - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.ConfigureClient }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient }; currentSession.Settings = currentSettings; sessionContext.ReconfigurationFilePath = resource.LocalPath; @@ -538,7 +538,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Repeat_MustFailWithInvalidUri() { var resource = new Uri("file:///C:/does/not/exist.txt"); - var settings = default(ApplicationSettings); + var settings = default(AppSettings); sessionContext.ReconfigurationFilePath = null; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); @@ -559,10 +559,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations [TestMethod] public void Repeat_MustAbortForSettingsPasswordIfWishedByUser() { - var currentSettings = new ApplicationSettings(); + var currentSettings = new AppSettings(); var location = Path.GetDirectoryName(GetType().Assembly.Location); var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME)); - var settings = new ApplicationSettings { ConfigurationMode = ConfigurationMode.ConfigureClient }; + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient }; currentSession.Settings = currentSettings; sessionContext.ReconfigurationFilePath = resource.LocalPath; diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/KioskModeOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/KioskModeOperationTests.cs index 5863e4a8..c16ef398 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/KioskModeOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/KioskModeOperationTests.cs @@ -21,12 +21,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public class KioskModeOperationTests { private SessionConfiguration currentSession; - private ApplicationSettings currentSettings; + private AppSettings currentSettings; private Mock desktopFactory; private Mock explorerShell; private Mock logger; private SessionConfiguration nextSession; - private ApplicationSettings nextSettings; + private AppSettings nextSettings; private Mock processFactory; private SessionContext sessionContext; @@ -36,12 +36,12 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Initialize() { currentSession = new SessionConfiguration(); - currentSettings = new ApplicationSettings(); + currentSettings = new AppSettings(); desktopFactory = new Mock(); explorerShell = new Mock(); logger = new Mock(); nextSession = new SessionConfiguration(); - nextSettings = new ApplicationSettings(); + nextSettings = new AppSettings(); processFactory = new Mock(); sessionContext = new SessionContext(); diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ServiceOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ServiceOperationTests.cs index 1e4a3418..7364a409 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/ServiceOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ServiceOperationTests.cs @@ -34,7 +34,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations private EventWaitHandle serviceEvent; private SessionConfiguration session; private SessionContext sessionContext; - private ApplicationSettings settings; + private AppSettings settings; private Mock userInfo; private ServiceOperation sut; @@ -50,7 +50,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations serviceEvent = new EventWaitHandle(false, EventResetMode.AutoReset, serviceEventName); session = new SessionConfiguration(); sessionContext = new SessionContext(); - settings = new ApplicationSettings(); + settings = new AppSettings(); userInfo = new Mock(); appConfig.ServiceEventName = serviceEventName; diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/SessionActivationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/SessionActivationOperationTests.cs index 62c8abb4..f117c49a 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/SessionActivationOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/SessionActivationOperationTests.cs @@ -9,10 +9,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Configuration.Contracts; -using SafeExamBrowser.Settings; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Operations; +using SafeExamBrowser.Settings; using SafeExamBrowser.Settings.Logging; namespace SafeExamBrowser.Runtime.UnitTests.Operations @@ -23,7 +23,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations private SessionConfiguration currentSession; private Mock logger; private SessionConfiguration nextSession; - private ApplicationSettings nextSettings; + private AppSettings nextSettings; private SessionContext sessionContext; private SessionActivationOperation sut; @@ -34,7 +34,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations currentSession = new SessionConfiguration(); logger = new Mock(); nextSession = new SessionConfiguration(); - nextSettings = new ApplicationSettings(); + nextSettings = new AppSettings(); sessionContext = new SessionContext(); nextSession.Settings = nextSettings; diff --git a/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs b/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs index 2f76fca5..a16b7df6 100644 --- a/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/RuntimeControllerTests.cs @@ -36,11 +36,11 @@ namespace SafeExamBrowser.Runtime.UnitTests private Mock clientProcess; private Mock clientProxy; private SessionConfiguration currentSession; - private ApplicationSettings currentSettings; + private AppSettings currentSettings; private Mock logger; private Mock messageBox; private SessionConfiguration nextSession; - private ApplicationSettings nextSettings; + private AppSettings nextSettings; private Mock shutdown; private Mock text; private Mock uiFactory; @@ -58,11 +58,11 @@ namespace SafeExamBrowser.Runtime.UnitTests clientProcess = new Mock(); clientProxy = new Mock(); currentSession = new SessionConfiguration(); - currentSettings = new ApplicationSettings(); + currentSettings = new AppSettings(); logger = new Mock(); messageBox = new Mock(); nextSession = new SessionConfiguration(); - nextSettings = new ApplicationSettings(); + nextSettings = new AppSettings(); runtimeHost = new Mock(); service = new Mock(); sessionContext = new SessionContext(); @@ -134,7 +134,7 @@ namespace SafeExamBrowser.Runtime.UnitTests var args = new ClientConfigurationEventArgs(); var nextAppConfig = new AppConfig(); var nextSessionId = Guid.NewGuid(); - var nextSettings = new ApplicationSettings(); + var nextSettings = new AppSettings(); nextSession.AppConfig = nextAppConfig; nextSession.SessionId = nextSessionId; diff --git a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs index 6da4cf7b..c44b83fa 100644 --- a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs @@ -107,7 +107,7 @@ namespace SafeExamBrowser.Runtime.Operations { var currentPassword = default(string); var passwordParams = default(PasswordParameters); - var settings = default(ApplicationSettings); + var settings = default(AppSettings); var status = default(LoadStatus?); if (source == UriSource.CommandLine) @@ -161,7 +161,7 @@ namespace SafeExamBrowser.Runtime.Operations } } - private OperationResult DetermineLoadResult(Uri uri, UriSource source, ApplicationSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string)) + private OperationResult DetermineLoadResult(Uri uri, UriSource source, AppSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string)) { if (status == LoadStatus.LoadWithBrowser || status == LoadStatus.Success) { @@ -218,7 +218,7 @@ namespace SafeExamBrowser.Runtime.Operations return OperationResult.Failed; } - private LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out ApplicationSettings settings, string currentPassword = default(string)) + private LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out AppSettings settings, string currentPassword = default(string)) { passwordParams = new PasswordParameters { Password = string.Empty, IsHash = true }; diff --git a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs index 963f0452..20c555e8 100644 --- a/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/KioskModeOperation.cs @@ -6,11 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -using SafeExamBrowser.Settings; using SafeExamBrowser.Core.Contracts.OperationModel; using SafeExamBrowser.Core.Contracts.OperationModel.Events; using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; +using SafeExamBrowser.Settings; using SafeExamBrowser.WindowsApi.Contracts; namespace SafeExamBrowser.Runtime.Operations diff --git a/SafeExamBrowser.Runtime/Operations/SessionOperation.cs b/SafeExamBrowser.Runtime/Operations/SessionOperation.cs index 39c91be9..9c4c3476 100644 --- a/SafeExamBrowser.Runtime/Operations/SessionOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/SessionOperation.cs @@ -21,9 +21,9 @@ namespace SafeExamBrowser.Runtime.Operations public abstract event ActionRequiredEventHandler ActionRequired; public abstract event StatusChangedEventHandler StatusChanged; - public SessionOperation(SessionContext sessionContext) + public SessionOperation(SessionContext context) { - Context = sessionContext; + Context = context; } public abstract OperationResult Perform(); diff --git a/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs index a92c9fe7..66d1702c 100644 --- a/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs +++ b/SafeExamBrowser.Service.UnitTests/Operations/LockdownOperationTests.cs @@ -27,7 +27,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations private Mock factory; private Mock monitor; private Mock logger; - private ApplicationSettings settings; + private AppSettings settings; private SessionContext sessionContext; private LockdownOperation sut; @@ -38,7 +38,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations factory = new Mock(); monitor = new Mock(); logger = new Mock(); - settings = new ApplicationSettings(); + settings = new AppSettings(); sessionContext = new SessionContext { Configuration = new ServiceConfiguration { Settings = settings, UserName = "TestName", UserSid = "S-1-234-TEST" } diff --git a/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs index f544942c..373bb7f9 100644 --- a/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs +++ b/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs @@ -42,7 +42,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations sessionContext.Configuration = new ServiceConfiguration { AppConfig = new AppConfig { ServiceEventName = $"{nameof(SafeExamBrowser)}-{nameof(SessionInitializationOperationTests)}" }, - Settings = new ApplicationSettings() + Settings = new AppSettings() }; sut = new SessionInitializationOperation(logger.Object, serviceEventFactory.Object, sessionContext); diff --git a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs index 738adbf0..24080c5b 100644 --- a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs +++ b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs @@ -82,7 +82,7 @@ namespace SafeExamBrowser.Service.UnitTests { AppConfig = new AppConfig { ServiceLogFilePath = "Test.log" }, SessionId = Guid.NewGuid(), - Settings = new ApplicationSettings { LogLevel = LogLevel.Warning } + Settings = new AppSettings { LogLevel = LogLevel.Warning } } }; diff --git a/SafeExamBrowser.Settings/ApplicationSettings.cs b/SafeExamBrowser.Settings/AppSettings.cs similarity index 90% rename from SafeExamBrowser.Settings/ApplicationSettings.cs rename to SafeExamBrowser.Settings/AppSettings.cs index 4478cff8..edc46336 100644 --- a/SafeExamBrowser.Settings/ApplicationSettings.cs +++ b/SafeExamBrowser.Settings/AppSettings.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Settings.Applications; using SafeExamBrowser.Settings.Browser; using SafeExamBrowser.Settings.Logging; using SafeExamBrowser.Settings.Monitoring; @@ -20,7 +21,7 @@ namespace SafeExamBrowser.Settings /// Defines all settings for the application. /// [Serializable] - public class ApplicationSettings + public class AppSettings { /// /// All action center-related settings. @@ -37,6 +38,11 @@ namespace SafeExamBrowser.Settings /// public bool AllowApplicationLogAccess { get; set; } + /// + /// All settings related to third-party applications. + /// + public ApplicationSettings Applications { get; set; } + /// /// All audio-related settings. /// @@ -92,9 +98,10 @@ namespace SafeExamBrowser.Settings /// public UserInterfaceMode UserInterfaceMode { get; set; } - public ApplicationSettings() + public AppSettings() { ActionCenter = new ActionCenterSettings(); + Applications = new ApplicationSettings(); Audio = new AudioSettings(); Browser = new BrowserSettings(); Keyboard = new KeyboardSettings(); diff --git a/SafeExamBrowser.Settings/Applications/ApplicationSettings.cs b/SafeExamBrowser.Settings/Applications/ApplicationSettings.cs new file mode 100644 index 00000000..4af0c563 --- /dev/null +++ b/SafeExamBrowser.Settings/Applications/ApplicationSettings.cs @@ -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 +{ + /// + /// TODO + /// + [Serializable] + public class ApplicationSettings + { + /// + /// + /// + public IList Blacklist { get; set; } + + /// + /// + /// + public IList Whitelist { get; set; } + + public ApplicationSettings() + { + Blacklist = new List(); + Whitelist = new List(); + } + } +} diff --git a/SafeExamBrowser.Settings/Applications/BlacklistApplication.cs b/SafeExamBrowser.Settings/Applications/BlacklistApplication.cs new file mode 100644 index 00000000..b7e19013 --- /dev/null +++ b/SafeExamBrowser.Settings/Applications/BlacklistApplication.cs @@ -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 +{ + /// + /// TODO + /// + [Serializable] + public class BlacklistApplication + { + /// + /// + /// + public bool AutoTerminate { get; set; } + + /// + /// + /// + public string ExecutableName { get; set; } + + /// + /// + /// + public string ExecutableOriginalName { get; set; } + } +} diff --git a/SafeExamBrowser.Monitoring.Contracts/Windows/Events/WindowChangedEventHandler.cs b/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs similarity index 61% rename from SafeExamBrowser.Monitoring.Contracts/Windows/Events/WindowChangedEventHandler.cs rename to SafeExamBrowser.Settings/Applications/WhitelistApplication.cs index b3d34b3d..db36d35e 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Windows/Events/WindowChangedEventHandler.cs +++ b/SafeExamBrowser.Settings/Applications/WhitelistApplication.cs @@ -8,10 +8,13 @@ using System; -namespace SafeExamBrowser.Monitoring.Contracts.Windows.Events +namespace SafeExamBrowser.Settings.Applications { /// - /// Indicates that the input focus has changed to the window with the specified handle. + /// TODO /// - public delegate void WindowChangedEventHandler(IntPtr window); + [Serializable] + public class WhitelistApplication + { + } } diff --git a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj index da22b7b5..a3886e84 100644 --- a/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj +++ b/SafeExamBrowser.Settings/SafeExamBrowser.Settings.csproj @@ -53,6 +53,9 @@ + + + @@ -67,11 +70,12 @@ - + + \ No newline at end of file diff --git a/SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyModifier.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyModifier.cs similarity index 74% rename from SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyModifier.cs rename to SafeExamBrowser.WindowsApi.Contracts/Events/KeyModifier.cs index ca905929..a1031cec 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyModifier.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyModifier.cs @@ -8,10 +8,10 @@ using System; -namespace SafeExamBrowser.Monitoring.Contracts.Keyboard +namespace SafeExamBrowser.WindowsApi.Contracts.Events { /// - /// The key modifiers which can be detected by the . + /// The key modifiers which can be detected by a keyboard hook. /// [Flags] public enum KeyModifier diff --git a/SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyState.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyState.cs similarity index 73% rename from SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyState.cs rename to SafeExamBrowser.WindowsApi.Contracts/Events/KeyState.cs index ee005c53..73d8c3c1 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Keyboard/KeyState.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyState.cs @@ -6,10 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Monitoring.Contracts.Keyboard +namespace SafeExamBrowser.WindowsApi.Contracts.Events { /// - /// The key states which can be detected by the . + /// The key states which can be detected by a keyboard hook. /// public enum KeyState { diff --git a/SafeExamBrowser.WindowsApi.Contracts/Events/KeyboardHookCallback.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyboardHookCallback.cs new file mode 100644 index 00000000..03188623 --- /dev/null +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/KeyboardHookCallback.cs @@ -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 +{ + /// + /// The callback for a keyboard hook. Return true to consume (i.e. block) the user input, otherwise false. + /// + public delegate bool KeyboardHookCallback(int keyCode, KeyModifier modifier, KeyState state); +} diff --git a/SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButton.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseButton.cs similarity index 75% rename from SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButton.cs rename to SafeExamBrowser.WindowsApi.Contracts/Events/MouseButton.cs index 0b3d29f6..e91494cc 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButton.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseButton.cs @@ -6,10 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Monitoring.Contracts.Mouse +namespace SafeExamBrowser.WindowsApi.Contracts.Events { /// - /// The mouse buttons which can be detected by the . + /// The mouse buttons which can be detected by a mouse hook. /// public enum MouseButton { diff --git a/SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButtonState.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseButtonState.cs similarity index 73% rename from SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButtonState.cs rename to SafeExamBrowser.WindowsApi.Contracts/Events/MouseButtonState.cs index 9af16397..a4e9e4a1 100644 --- a/SafeExamBrowser.Monitoring.Contracts/Mouse/MouseButtonState.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseButtonState.cs @@ -6,10 +6,10 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -namespace SafeExamBrowser.Monitoring.Contracts.Mouse +namespace SafeExamBrowser.WindowsApi.Contracts.Events { /// - /// The mouse button states which can be detected by the . + /// The mouse button states which can be detected a mouse hook. /// public enum MouseButtonState { diff --git a/SafeExamBrowser.WindowsApi.Contracts/Events/MouseHookCallback.cs b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseHookCallback.cs new file mode 100644 index 00000000..1a5c9f65 --- /dev/null +++ b/SafeExamBrowser.WindowsApi.Contracts/Events/MouseHookCallback.cs @@ -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 +{ + /// + /// The callback for a mouse hook. Return true to consume (i.e. block) the user input, otherwise false. + /// + public delegate bool MouseHookCallback(MouseButton button, MouseButtonState state); +} diff --git a/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs b/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs index 1db3b063..164b12a3 100644 --- a/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/IDesktopFactory.cs @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - namespace SafeExamBrowser.WindowsApi.Contracts { /// diff --git a/SafeExamBrowser.WindowsApi.Contracts/INativeMethods.cs b/SafeExamBrowser.WindowsApi.Contracts/INativeMethods.cs index 040217c9..255e3a56 100644 --- a/SafeExamBrowser.WindowsApi.Contracts/INativeMethods.cs +++ b/SafeExamBrowser.WindowsApi.Contracts/INativeMethods.cs @@ -8,8 +8,6 @@ using System; using System.Collections.Generic; -using SafeExamBrowser.Monitoring.Contracts.Keyboard; -using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.WindowsApi.Contracts.Events; namespace SafeExamBrowser.WindowsApi.Contracts @@ -20,20 +18,20 @@ namespace SafeExamBrowser.WindowsApi.Contracts public interface INativeMethods { /// - /// Deregisters the system hook for the given keyboard interceptor. + /// Deregisters a previously registered keyboard hook. /// /// - /// If the hook for the given interceptor could not be successfully removed. + /// If the hook could not be successfully removed. /// - void DeregisterKeyboardHook(IKeyboardInterceptor interceptor); + void DeregisterKeyboardHook(Guid hookId); /// - /// Deregisters the system hook for the given mouse interceptor. + /// Deregisters a previously registered mouse hook. /// /// - /// If the hook for the given interceptor could not be successfully removed. + /// If the hook could not be successfully removed. /// - void DeregisterMouseHook(IMouseInterceptor interceptor); + void DeregisterMouseHook(Guid hookId); /// /// Deregisters a previously registered system event hook. @@ -65,14 +63,12 @@ namespace SafeExamBrowser.WindowsApi.Contracts uint GetProcessIdFor(IntPtr window); /// - /// Retrieves a window handle to the Windows taskbar. Returns IntPtr.Zero - /// if the taskbar could not be found (i.e. if it isn't running). + /// Retrieves a window handle to the Windows taskbar. Returns IntPtr.Zero if the taskbar could not be found (i.e. if it isn't running). /// IntPtr GetShellWindowHandle(); /// - /// Retrieves the process ID of the main Windows explorer instance controlling - /// desktop and taskbar or 0, if the process isn't running. + /// Retrieves the process identifier of the main Windows explorer instance controlling desktop and taskbar or 0, if the process isn't running. /// uint GetShellProcessId(); @@ -121,24 +117,24 @@ namespace SafeExamBrowser.WindowsApi.Contracts void PreventSleepMode(); /// - /// 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. /// - void RegisterKeyboardHook(IKeyboardInterceptor interceptor); + Guid RegisterKeyboardHook(KeyboardHookCallback callback); /// - /// 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. /// - void RegisterMouseHook(IMouseInterceptor interceptor); + Guid RegisterMouseHook(MouseHookCallback callback); /// - /// Registers a system event which will invoke the specified callback when a window has received mouse capture. - /// Returns the ID of the newly registered Windows event hook. + /// Registers a system event which will invoke the specified callback when a window has received mouse capture. Returns the identifier of + /// the newly registered Windows event hook. /// Guid RegisterSystemCaptureStartEvent(SystemEventCallback callback); /// - /// Registers a system event which will invoke the specified callback when the foreground window has changed. - /// Returns a handle to the newly registered Windows event hook. + /// Registers a system event which will invoke the specified callback when the foreground window has changed. Returns the identifier of the + /// newly registered Windows event hook. /// Guid RegisterSystemForegroundEvent(SystemEventCallback callback); @@ -156,7 +152,7 @@ namespace SafeExamBrowser.WindowsApi.Contracts void RestoreWindow(IntPtr window); /// - /// Attempts to resume the thread referenced by the given thread ID. Returns true if the thread was successfully resumed, + /// Attempts to resume the thread referenced by the given thread identifier. Returns true if the thread was successfully resumed, /// otherwise false. /// bool ResumeThread(int threadId); @@ -183,7 +179,7 @@ namespace SafeExamBrowser.WindowsApi.Contracts void SetWorkingArea(IBounds bounds); /// - /// Attempts to suspend the thread referenced by the given thread ID. Returns true if the thread was successfully suspended, + /// Attempts to suspend the thread referenced by the given thread identifier. Returns true if the thread was successfully suspended, /// otherwise false. /// bool SuspendThread(int threadId); diff --git a/SafeExamBrowser.WindowsApi.Contracts/SafeExamBrowser.WindowsApi.Contracts.csproj b/SafeExamBrowser.WindowsApi.Contracts/SafeExamBrowser.WindowsApi.Contracts.csproj index d75f417f..1207dd11 100644 --- a/SafeExamBrowser.WindowsApi.Contracts/SafeExamBrowser.WindowsApi.Contracts.csproj +++ b/SafeExamBrowser.WindowsApi.Contracts/SafeExamBrowser.WindowsApi.Contracts.csproj @@ -53,6 +53,12 @@ + + + + + + @@ -66,11 +72,5 @@ - - - {6d563a30-366d-4c35-815b-2c9e6872278b} - SafeExamBrowser.Monitoring.Contracts - - \ No newline at end of file diff --git a/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs b/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs index c99a1f2a..16d945af 100644 --- a/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/KeyboardHook.cs @@ -8,8 +8,8 @@ using System; using System.Runtime.InteropServices; -using SafeExamBrowser.Monitoring.Contracts.Keyboard; using SafeExamBrowser.WindowsApi.Constants; +using SafeExamBrowser.WindowsApi.Contracts.Events; using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Types; @@ -18,14 +18,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks internal class KeyboardHook { private bool altPressed, ctrlPressed; + private KeyboardHookCallback callback; + private IntPtr handle; private HookDelegate hookDelegate; - internal IntPtr Handle { get; private set; } - internal IKeyboardInterceptor Interceptor { get; private set; } + internal Guid Id { get; private set; } - internal KeyboardHook(IKeyboardInterceptor interceptor) + internal KeyboardHook(KeyboardHookCallback callback) { - Interceptor = interceptor; + this.callback = callback; + this.Id = Guid.NewGuid(); } internal void Attach() @@ -38,13 +40,12 @@ namespace SafeExamBrowser.WindowsApi.Hooks // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Not doing so will result in a CallbackOnCollectedDelegate error and subsequent application crash! hookDelegate = new HookDelegate(LowLevelKeyboardProc); - - Handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0); + handle = User32.SetWindowsHookEx(HookType.WH_KEYBOARD_LL, hookDelegate, moduleHandle, 0); } internal bool Detach() { - return User32.UnhookWindowsHookEx(Handle); + return User32.UnhookWindowsHookEx(handle); } private IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam) @@ -55,13 +56,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks var modifier = GetModifiers(keyData, wParam.ToInt32()); var state = GetState(wParam.ToInt32()); - if (Interceptor.Block((int) keyData.KeyCode, modifier, state)) + if (callback((int) keyData.KeyCode, modifier, state)) { return (IntPtr) 1; } } - return User32.CallNextHookEx(Handle, nCode, wParam, lParam); + return User32.CallNextHookEx(handle, nCode, wParam, lParam); } private KeyState GetState(int wParam) diff --git a/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs b/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs index 532ecebc..815bb8e0 100644 --- a/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/MouseHook.cs @@ -8,8 +8,8 @@ using System; using System.Runtime.InteropServices; -using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.WindowsApi.Constants; +using SafeExamBrowser.WindowsApi.Contracts.Events; using SafeExamBrowser.WindowsApi.Delegates; using SafeExamBrowser.WindowsApi.Types; @@ -17,14 +17,16 @@ namespace SafeExamBrowser.WindowsApi.Hooks { internal class MouseHook { + private MouseHookCallback callback; + private IntPtr handle; private HookDelegate hookDelegate; - internal IntPtr Handle { get; private set; } - internal IMouseInterceptor Interceptor { get; private set; } + internal Guid Id { get; private set; } - internal MouseHook(IMouseInterceptor interceptor) + internal MouseHook(MouseHookCallback callback) { - Interceptor = interceptor; + this.callback = callback; + this.Id = Guid.NewGuid(); } internal void Attach() @@ -37,13 +39,12 @@ namespace SafeExamBrowser.WindowsApi.Hooks // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Not doing so will result in a CallbackOnCollectedDelegate error and subsequent application crash! hookDelegate = new HookDelegate(LowLevelMouseProc); - - Handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0); + handle = User32.SetWindowsHookEx(HookType.WH_MOUSE_LL, hookDelegate, moduleHandle, 0); } internal bool Detach() { - return User32.UnhookWindowsHookEx(Handle); + return User32.UnhookWindowsHookEx(handle); } private IntPtr LowLevelMouseProc(int nCode, IntPtr wParam, IntPtr lParam) @@ -54,13 +55,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks var button = GetButton(wParam.ToInt32()); var state = GetState(wParam.ToInt32()); - if (Interceptor.Block(button, state)) + if (callback(button, state)) { return (IntPtr) 1; } } - return User32.CallNextHookEx(Handle, nCode, wParam, lParam); + return User32.CallNextHookEx(handle, nCode, wParam, lParam); } private bool Ignore(int wParam) diff --git a/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs b/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs index 95073852..2e4d1867 100644 --- a/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs +++ b/SafeExamBrowser.WindowsApi/Hooks/SystemHook.cs @@ -8,8 +8,8 @@ using System; using System.Threading; -using SafeExamBrowser.WindowsApi.Contracts.Events; using SafeExamBrowser.WindowsApi.Constants; +using SafeExamBrowser.WindowsApi.Contracts.Events; using SafeExamBrowser.WindowsApi.Delegates; namespace SafeExamBrowser.WindowsApi.Hooks @@ -21,8 +21,8 @@ namespace SafeExamBrowser.WindowsApi.Hooks private bool detachSuccess; private EventDelegate eventDelegate; private uint eventId; + private IntPtr handle; - internal IntPtr Handle { get; private set; } internal Guid Id { get; private set; } public SystemHook(SystemEventCallback callback, uint eventId) @@ -40,14 +40,13 @@ namespace SafeExamBrowser.WindowsApi.Hooks // Ensures that the hook delegate does not get garbage collected prematurely, as it will be passed to unmanaged code. // Not doing so will result in a CallbackOnCollectedDelegate error and subsequent application crash! eventDelegate = new EventDelegate(LowLevelSystemProc); - - Handle = User32.SetWinEventHook(eventId, eventId, IntPtr.Zero, eventDelegate, 0, 0, Constant.WINEVENT_OUTOFCONTEXT); + handle = User32.SetWinEventHook(eventId, eventId, IntPtr.Zero, eventDelegate, 0, 0, Constant.WINEVENT_OUTOFCONTEXT); } internal void AwaitDetach() { detachEvent.WaitOne(); - detachSuccess = User32.UnhookWinEvent(Handle); + detachSuccess = User32.UnhookWinEvent(handle); detachResultAvailableEvent.Set(); } diff --git a/SafeExamBrowser.WindowsApi/NativeMethods.cs b/SafeExamBrowser.WindowsApi/NativeMethods.cs index 09eac477..70402fa7 100644 --- a/SafeExamBrowser.WindowsApi/NativeMethods.cs +++ b/SafeExamBrowser.WindowsApi/NativeMethods.cs @@ -14,8 +14,6 @@ using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading; -using SafeExamBrowser.Monitoring.Contracts.Keyboard; -using SafeExamBrowser.Monitoring.Contracts.Mouse; using SafeExamBrowser.WindowsApi.Constants; using SafeExamBrowser.WindowsApi.Contracts; using SafeExamBrowser.WindowsApi.Contracts.Events; @@ -26,34 +24,34 @@ namespace SafeExamBrowser.WindowsApi { public class NativeMethods : INativeMethods { - private ConcurrentDictionary KeyboardHooks = new ConcurrentDictionary(); - private ConcurrentDictionary MouseHooks = new ConcurrentDictionary(); - private ConcurrentDictionary SystemHooks = new ConcurrentDictionary(); + private ConcurrentBag KeyboardHooks = new ConcurrentBag(); + private ConcurrentBag MouseHooks = new ConcurrentBag(); + private ConcurrentBag SystemHooks = new ConcurrentBag(); /// /// Upon finalization, unregister all active system events and hooks... /// ~NativeMethods() { - foreach (var hook in SystemHooks.Values) + foreach (var hook in SystemHooks) { hook.Detach(); } - foreach (var hook in KeyboardHooks.Values) + foreach (var hook in KeyboardHooks) { hook.Detach(); } - foreach (var hook in MouseHooks.Values) + foreach (var hook in MouseHooks) { hook.Detach(); } } - public void DeregisterKeyboardHook(IKeyboardInterceptor interceptor) + public void DeregisterKeyboardHook(Guid hookId) { - var hook = KeyboardHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor); + var hook = KeyboardHooks.FirstOrDefault(h => h.Id == hookId); if (hook != null) { @@ -64,13 +62,13 @@ namespace SafeExamBrowser.WindowsApi throw new Win32Exception(Marshal.GetLastWin32Error()); } - KeyboardHooks.TryRemove(hook.Handle, out _); + KeyboardHooks.TryTake(out _); } } - public void DeregisterMouseHook(IMouseInterceptor interceptor) + public void DeregisterMouseHook(Guid hookId) { - var hook = MouseHooks.Values.FirstOrDefault(h => h.Interceptor == interceptor); + var hook = MouseHooks.FirstOrDefault(h => h.Id == hookId); if (hook != null) { @@ -81,13 +79,13 @@ namespace SafeExamBrowser.WindowsApi throw new Win32Exception(Marshal.GetLastWin32Error()); } - MouseHooks.TryRemove(hook.Handle, out _); + MouseHooks.TryTake(out _); } } public void DeregisterSystemEventHook(Guid hookId) { - var hook = SystemHooks.Values.FirstOrDefault(h => h.Id == hookId); + var hook = SystemHooks.FirstOrDefault(h => h.Id == hookId); if (hook != null) { @@ -98,7 +96,7 @@ namespace SafeExamBrowser.WindowsApi throw new Win32Exception(Marshal.GetLastWin32Error()); } - SystemHooks.TryRemove(hook.Handle, out _); + SystemHooks.TryTake(out _); } } @@ -236,16 +234,18 @@ namespace SafeExamBrowser.WindowsApi Kernel32.SetThreadExecutionState(EXECUTION_STATE.CONTINUOUS | EXECUTION_STATE.DISPLAY_REQUIRED | EXECUTION_STATE.SYSTEM_REQUIRED); } - public void RegisterKeyboardHook(IKeyboardInterceptor interceptor) + public Guid RegisterKeyboardHook(KeyboardHookCallback callback) { + var hookId = default(Guid); var hookReadyEvent = new AutoResetEvent(false); var hookThread = new Thread(() => { - var hook = new KeyboardHook(interceptor); + var hook = new KeyboardHook(callback); var sleepEvent = new AutoResetEvent(false); hook.Attach(); - KeyboardHooks[hook.Handle] = hook; + hookId = hook.Id; + KeyboardHooks.Add(hook); hookReadyEvent.Set(); while (true) @@ -259,18 +259,22 @@ namespace SafeExamBrowser.WindowsApi hookThread.Start(); hookReadyEvent.WaitOne(); + + return hookId; } - public void RegisterMouseHook(IMouseInterceptor interceptor) + public Guid RegisterMouseHook(MouseHookCallback callback) { + var hookId = default(Guid); var hookReadyEvent = new AutoResetEvent(false); var hookThread = new Thread(() => { - var hook = new MouseHook(interceptor); + var hook = new MouseHook(callback); var sleepEvent = new AutoResetEvent(false); hook.Attach(); - MouseHooks[hook.Handle] = hook; + hookId = hook.Id; + MouseHooks.Add(hook); hookReadyEvent.Set(); while (true) @@ -284,6 +288,8 @@ namespace SafeExamBrowser.WindowsApi hookThread.Start(); hookReadyEvent.WaitOne(); + + return hookId; } public Guid RegisterSystemCaptureStartEvent(SystemEventCallback callback) @@ -306,7 +312,7 @@ namespace SafeExamBrowser.WindowsApi hook.Attach(); hookId = hook.Id; - SystemHooks[hook.Handle] = hook; + SystemHooks.Add(hook); hookReadyEvent.Set(); hook.AwaitDetach(); }); diff --git a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj index 9475f2b3..13b13469 100644 --- a/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj +++ b/SafeExamBrowser.WindowsApi/SafeExamBrowser.WindowsApi.csproj @@ -96,10 +96,6 @@ {64ea30fb-11d4-436a-9c2b-88566285363e} SafeExamBrowser.Logging.Contracts - - {6d563a30-366d-4c35-815b-2c9e6872278b} - SafeExamBrowser.Monitoring.Contracts - {c7889e97-6ff6-4a58-b7cb-521ed276b316} SafeExamBrowser.UserInterface.Contracts