diff --git a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs
index 45d769f1..59d02052 100644
--- a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs
+++ b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs
@@ -26,14 +26,14 @@ namespace SafeExamBrowser.Contracts.Lockdown
Guid GroupId { get; }
///
- /// Disables the feature.
+ /// Disables the feature. Returns true if successful, otherwise false.
///
- void DisableFeature();
+ bool DisableFeature();
///
- /// Enables the feature.
+ /// Enables the feature. Returns true if successful, otherwise false.
///
- void EnableFeature();
+ bool EnableFeature();
///
/// Starts monitoring the feature to ensure that it remains as currently configured.
@@ -41,8 +41,9 @@ namespace SafeExamBrowser.Contracts.Lockdown
void Monitor();
///
- /// Restores the feature to its previous configuration (i.e. before it was enabled or disabled).
+ /// Restores the feature to its previous configuration (i.e. before it was enabled or disabled). Returns true if successful,
+ /// otherwise false.
///
- void Restore();
+ bool Restore();
}
}
diff --git a/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs b/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs
new file mode 100644
index 00000000..2ca3be4d
--- /dev/null
+++ b/SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs
@@ -0,0 +1,130 @@
+/*
+ * 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;
+using System.Threading;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using Moq;
+using SafeExamBrowser.Contracts.Lockdown;
+using SafeExamBrowser.Contracts.Logging;
+
+namespace SafeExamBrowser.Lockdown.UnitTests
+{
+ [TestClass]
+ public class AutoRestoreMechanismTests
+ {
+ private Mock backup;
+ private Mock logger;
+ private AutoRestoreMechanism sut;
+
+ [TestInitialize]
+ public void Initialize()
+ {
+ backup = new Mock();
+ logger = new Mock();
+
+ sut = new AutoRestoreMechanism(backup.Object, logger.Object, 0);
+ }
+
+ [TestMethod]
+ public void MustExecuteAsynchronously()
+ {
+ var sync = new AutoResetEvent(false);
+ var threadId = Thread.CurrentThread.ManagedThreadId;
+
+ backup.Setup(b => b.GetAllConfigurations()).Callback(() => { threadId = Thread.CurrentThread.ManagedThreadId; sync.Set(); });
+
+ sut.Start();
+ sync.WaitOne();
+ sut.Stop();
+
+ Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
+ }
+
+ [TestMethod]
+ public void MustNotTerminateUntilAllChangesReverted()
+ {
+ var configuration = new Mock();
+ var counter = 0;
+ var limit = new Random().Next(5, 50);
+ var list = new List { configuration.Object };
+ var sync = new AutoResetEvent(false);
+
+ backup.Setup(b => b.GetAllConfigurations()).Returns(list).Callback(() => counter++);
+ backup.Setup(b => b.Delete(It.IsAny())).Callback(() => { list.Clear(); sync.Set(); });
+ configuration.Setup(c => c.Restore()).Returns(() => counter == limit);
+
+ sut.Start();
+ sync.WaitOne();
+ sut.Stop();
+
+ backup.Verify(b => b.GetAllConfigurations(), Times.Exactly(limit));
+ backup.Verify(b => b.Delete(It.Is(c => c == configuration.Object)), Times.Once);
+ configuration.Verify(c => c.Restore(), Times.Exactly(limit));
+ }
+
+ [TestMethod]
+ public void MustRespectTimeout()
+ {
+ const int TIMEOUT = 50;
+
+ var after = default(DateTime);
+ var before = default(DateTime);
+ var configuration = new Mock();
+ var counter = 0;
+ var list = new List { configuration.Object };
+ var sync = new AutoResetEvent(false);
+
+ sut = new AutoRestoreMechanism(backup.Object, logger.Object, TIMEOUT);
+
+ backup.Setup(b => b.GetAllConfigurations()).Returns(list).Callback(() =>
+ {
+ switch (++counter)
+ {
+ case 1:
+ before = DateTime.Now;
+ break;
+ case 2:
+ after = DateTime.Now;
+ list.Clear();
+ sync.Set();
+ break;
+ }
+ });
+
+ sut.Start();
+ sync.WaitOne();
+ sut.Stop();
+
+ Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
+ }
+
+ [TestMethod]
+ public void MustStop()
+ {
+ var configuration = new Mock();
+ var counter = 0;
+ var list = new List { configuration.Object };
+
+ backup.Setup(b => b.GetAllConfigurations()).Returns(list).Callback(() => counter++);
+
+ sut.Start();
+ Thread.Sleep(25);
+ sut.Stop();
+
+ backup.Verify(b => b.GetAllConfigurations(), Times.Between(counter, counter + 1, Range.Inclusive));
+ }
+
+ [TestMethod]
+ public void MustValidateTimeout()
+ {
+ Assert.ThrowsException(() => new AutoRestoreMechanism(backup.Object, logger.Object, new Random().Next(int.MinValue, -1)));
+ }
+ }
+}
diff --git a/SafeExamBrowser.Lockdown.UnitTests/SafeExamBrowser.Lockdown.UnitTests.csproj b/SafeExamBrowser.Lockdown.UnitTests/SafeExamBrowser.Lockdown.UnitTests.csproj
index ceeaca29..ac6b6a7e 100644
--- a/SafeExamBrowser.Lockdown.UnitTests/SafeExamBrowser.Lockdown.UnitTests.csproj
+++ b/SafeExamBrowser.Lockdown.UnitTests/SafeExamBrowser.Lockdown.UnitTests.csproj
@@ -57,20 +57,46 @@
MinimumRecommendedRules.ruleset
+
+ ..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll
+
..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll
..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll
+
+ ..\packages\Moq.4.12.0\lib\net45\Moq.dll
+
+
+
+ ..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll
+
+
+ ..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll
+
+
-
+
+ Designer
+
+
+
+
+ {47da5933-bef8-4729-94e6-abde2db12262}
+ SafeExamBrowser.Contracts
+
+
+ {386b6042-3e12-4753-9fc6-c88ea4f97030}
+ SafeExamBrowser.Lockdown
+
diff --git a/SafeExamBrowser.Lockdown.UnitTests/packages.config b/SafeExamBrowser.Lockdown.UnitTests/packages.config
index 2f7c5a18..18056a53 100644
--- a/SafeExamBrowser.Lockdown.UnitTests/packages.config
+++ b/SafeExamBrowser.Lockdown.UnitTests/packages.config
@@ -1,5 +1,9 @@
+
+
+
+
\ No newline at end of file
diff --git a/SafeExamBrowser.Lockdown/AutoRestoreMechanism.cs b/SafeExamBrowser.Lockdown/AutoRestoreMechanism.cs
index 9d042abd..fc9d7b40 100644
--- a/SafeExamBrowser.Lockdown/AutoRestoreMechanism.cs
+++ b/SafeExamBrowser.Lockdown/AutoRestoreMechanism.cs
@@ -6,27 +6,107 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
+using System.Linq;
+using System.Threading.Tasks;
using SafeExamBrowser.Contracts.Lockdown;
+using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown
{
public class AutoRestoreMechanism : IAutoRestoreMechanism
{
- private IFeatureConfigurationBackup backup;
+ private readonly object @lock = new object();
- public AutoRestoreMechanism(IFeatureConfigurationBackup backup)
+ private IFeatureConfigurationBackup backup;
+ private ILogger logger;
+ private bool running;
+ private int timeout_ms;
+
+ public AutoRestoreMechanism(IFeatureConfigurationBackup backup, ILogger logger, int timeout_ms)
{
+ if (timeout_ms < 0)
+ {
+ throw new ArgumentException("Must be 0 or greater!", nameof(timeout_ms));
+ }
+
this.backup = backup;
+ this.logger = logger;
+ this.timeout_ms = timeout_ms;
}
public void Start()
{
-
+ lock (@lock)
+ {
+ if (!running)
+ {
+ running = true;
+ Task.Run(new Action(RestoreAll));
+ logger.Info("Started auto-restore mechanism.");
+ }
+ else
+ {
+ logger.Info("Auto-restore mechanism is already running.");
+ }
+ }
}
public void Stop()
{
-
+ lock (@lock)
+ {
+ if (running)
+ {
+ running = false;
+ logger.Info("Stopped auto-restore mechanism.");
+ }
+ else
+ {
+ logger.Info("Auto-restore mechanism is not running.");
+ }
+ }
+ }
+
+ private void RestoreAll()
+ {
+ var configurations = backup.GetAllConfigurations();
+
+ if (!configurations.Any())
+ {
+ running = false;
+ logger.Info("Nothing to restore, stopped auto-restore mechanism.");
+
+ return;
+ }
+
+ logger.Info($"Attempting to restore {configurations.Count} items...");
+
+ foreach (var configuration in configurations)
+ {
+ var success = configuration.Restore();
+
+ if (success)
+ {
+ backup.Delete(configuration);
+ }
+ else
+ {
+ logger.Warn($"Failed to restore {configuration}!");
+ }
+
+ lock (@lock)
+ {
+ if (!running)
+ {
+ logger.Info("Auto-restore mechanism was aborted.");
+
+ return;
+ }
+ }
+ }
+
+ Task.Delay(timeout_ms).ContinueWith((_) => RestoreAll());
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurationBackup.cs b/SafeExamBrowser.Lockdown/FeatureConfigurationBackup.cs
index cb87b87a..7c786d1d 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurationBackup.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurationBackup.cs
@@ -45,7 +45,7 @@ namespace SafeExamBrowser.Lockdown
}
else
{
- logger.Warn($"Could not delete {configuration} as it does not exists in backup!");
+ logger.Warn($"Could not delete {configuration} as it does not exist in backup!");
}
}
}
@@ -78,25 +78,6 @@ namespace SafeExamBrowser.Lockdown
}
}
- private void SaveToFile(List configurations)
- {
- try
- {
- logger.Debug($"Attempting to save backup data to '{filePath}'...");
-
- using (var stream = File.Open(filePath, FileMode.Create))
- {
- new BinaryFormatter().Serialize(stream, configurations);
- }
-
- logger.Debug($"Successfully saved {configurations.Count} items.");
- }
- catch (Exception e)
- {
- logger.Error($"Failed to save backup data to '{filePath}'!", e);
- }
- }
-
private List LoadFromFile()
{
var configurations = new List();
@@ -128,5 +109,32 @@ namespace SafeExamBrowser.Lockdown
return configurations;
}
+
+ private void SaveToFile(List configurations)
+ {
+ try
+ {
+ if (configurations.Any())
+ {
+ logger.Debug($"Attempting to save backup data to '{filePath}'...");
+
+ using (var stream = File.Open(filePath, FileMode.Create))
+ {
+ new BinaryFormatter().Serialize(stream, configurations);
+ }
+
+ logger.Debug($"Successfully saved {configurations.Count} items.");
+ }
+ else
+ {
+ File.Delete(filePath);
+ logger.Debug("No backup data to save, deleted backup file.");
+ }
+ }
+ catch (Exception e)
+ {
+ logger.Error($"Failed to save backup data to '{filePath}'!", e);
+ }
+ }
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/ChromeNotificationConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/ChromeNotificationConfiguration.cs
index 490af30c..af50791e 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/ChromeNotificationConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/ChromeNotificationConfiguration.cs
@@ -18,14 +18,18 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
logger.Info("Disabling...");
+
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
logger.Info("Enabling...");
+
+ return true;
}
public override void Monitor()
@@ -33,9 +37,11 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
logger.Info("Monitoring...");
}
- public override void Restore()
+ public override bool Restore()
{
logger.Info("Restoring...");
+
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/EaseOfAccessConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/EaseOfAccessConfiguration.cs
index 3b876d95..bf3544b8 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/EaseOfAccessConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/EaseOfAccessConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs
index 63e00ddd..825ed412 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs
@@ -29,10 +29,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
this.logger = logger;
}
- public abstract void DisableFeature();
- public abstract void EnableFeature();
+ public abstract bool DisableFeature();
+ public abstract bool EnableFeature();
public abstract void Monitor();
- public abstract void Restore();
+ public abstract bool Restore();
public override string ToString()
{
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/NetworkOptionsConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/NetworkOptionsConfiguration.cs
index eedb2258..da522f62 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/NetworkOptionsConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/NetworkOptionsConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/PasswordChangeConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/PasswordChangeConfiguration.cs
index f8051df9..6f37e7a8 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/PasswordChangeConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/PasswordChangeConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/PowerOptionsConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/PowerOptionsConfiguration.cs
index f7be1c5d..f100a31b 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/PowerOptionsConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/PowerOptionsConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RemoteConnectionConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RemoteConnectionConfiguration.cs
index e1b15e0c..4a60ef07 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RemoteConnectionConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RemoteConnectionConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/SignoutConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/SignoutConfiguration.cs
index 43d78ea0..d6db15ab 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/SignoutConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/SignoutConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/TaskManagerConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/TaskManagerConfiguration.cs
index 960ef92f..8df5546a 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/TaskManagerConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/TaskManagerConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/UserLockConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/UserLockConfiguration.cs
index 03dd2578..39437c7c 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/UserLockConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/UserLockConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/UserSwitchConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/UserSwitchConfiguration.cs
index 602dbfbf..67330467 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/UserSwitchConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/UserSwitchConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/VmwareOverlayConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/VmwareOverlayConfiguration.cs
index 55454767..c8b17e82 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/VmwareOverlayConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/VmwareOverlayConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/WindowsUpdateConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/WindowsUpdateConfiguration.cs
index b89b4fbb..706e8a89 100644
--- a/SafeExamBrowser.Lockdown/FeatureConfigurations/WindowsUpdateConfiguration.cs
+++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/WindowsUpdateConfiguration.cs
@@ -18,14 +18,14 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
{
}
- public override void DisableFeature()
+ public override bool DisableFeature()
{
-
+ return true;
}
- public override void EnableFeature()
+ public override bool EnableFeature()
{
-
+ return true;
}
public override void Monitor()
@@ -33,9 +33,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
}
- public override void Restore()
+ public override bool Restore()
{
-
+ return true;
}
}
}
diff --git a/SafeExamBrowser.Service.UnitTests/Operations/SessionActivationOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/SessionActivationOperationTests.cs
index d57ace81..001e3194 100644
--- a/SafeExamBrowser.Service.UnitTests/Operations/SessionActivationOperationTests.cs
+++ b/SafeExamBrowser.Service.UnitTests/Operations/SessionActivationOperationTests.cs
@@ -45,14 +45,18 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
Assert.AreEqual(OperationResult.Success, result);
Assert.IsTrue(wasSet);
+ Assert.IsTrue(sessionContext.IsRunning);
}
[TestMethod]
public void Revert_MustDoNothing()
{
+ sessionContext.IsRunning = true;
+
var result = sut.Revert();
Assert.AreEqual(OperationResult.Success, result);
+ Assert.IsTrue(sessionContext.IsRunning);
}
}
}
diff --git a/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs b/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs
index 56f881d7..abe19126 100644
--- a/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs
+++ b/SafeExamBrowser.Service.UnitTests/Operations/SessionInitializationOperationTests.cs
@@ -112,6 +112,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
[TestMethod]
public void Revert_MustSetServiceEvent()
{
+ sessionContext.IsRunning = true;
sessionContext.ServiceEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
var wasSet = false;
@@ -124,6 +125,22 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
Assert.IsTrue(wasSet);
}
+ [TestMethod]
+ public void Revert_MustNotSetServiceEventIfNoSessionActive()
+ {
+ sessionContext.IsRunning = false;
+ sessionContext.ServiceEvent = new EventWaitHandle(false, EventResetMode.AutoReset);
+
+ var wasSet = false;
+ var task = Task.Run(() => wasSet = sessionContext.ServiceEvent.WaitOne(1000));
+ var result = sut.Revert();
+
+ task.Wait();
+
+ Assert.AreEqual(OperationResult.Success, result);
+ Assert.IsFalse(wasSet);
+ }
+
[TestMethod]
public void Revert_MustStartAutoRestoreMechanism()
{
@@ -132,5 +149,16 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
autoRestoreMechanism.Verify(m => m.Start(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
+
+ [TestMethod]
+ public void Revert_MustResetSessionFlag()
+ {
+ sessionContext.IsRunning = true;
+
+ var result = sut.Revert();
+
+ Assert.AreEqual(OperationResult.Success, result);
+ Assert.IsFalse(sessionContext.IsRunning);
+ }
}
}
diff --git a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs
index 066b6f9e..7816ab7b 100644
--- a/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs
+++ b/SafeExamBrowser.Service.UnitTests/ServiceControllerTests.cs
@@ -63,6 +63,7 @@ namespace SafeExamBrowser.Service.UnitTests
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionContext.Configuration = new ServiceConfiguration { SessionId = Guid.NewGuid() };
+ sessionContext.IsRunning = true;
sut.TryStart();
serviceHost.Raise(h => h.SessionStartRequested += null, args);
@@ -79,6 +80,7 @@ namespace SafeExamBrowser.Service.UnitTests
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
sessionContext.Configuration = new ServiceConfiguration { SessionId = args.SessionId };
+ sessionContext.IsRunning = true;
sut.TryStart();
serviceHost.Raise(h => h.SessionStopRequested += null, args);
@@ -106,6 +108,7 @@ namespace SafeExamBrowser.Service.UnitTests
var args = new SessionStopEventArgs { SessionId = Guid.NewGuid() };
bootstrapSequence.Setup(b => b.TryPerform()).Returns(OperationResult.Success);
+ sessionContext.IsRunning = false;
sut.TryStart();
serviceHost.Raise(h => h.SessionStopRequested += null, args);
@@ -145,6 +148,7 @@ namespace SafeExamBrowser.Service.UnitTests
bootstrapSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => bootstrap = ++order);
sessionSequence.Setup(b => b.TryRevert()).Returns(OperationResult.Success).Callback(() => session = ++order);
sessionContext.Configuration = new ServiceConfiguration();
+ sessionContext.IsRunning = true;
sut.Terminate();
diff --git a/SafeExamBrowser.Service/CompositionRoot.cs b/SafeExamBrowser.Service/CompositionRoot.cs
index d391edca..6d48b41c 100644
--- a/SafeExamBrowser.Service/CompositionRoot.cs
+++ b/SafeExamBrowser.Service/CompositionRoot.cs
@@ -36,11 +36,12 @@ namespace SafeExamBrowser.Service
internal void BuildObjectGraph()
{
const int FIVE_SECONDS = 5000;
- var backupFile = BuildBackupFilePath();
+
+ var backupFilePath = BuildBackupFilePath();
InitializeLogging();
- var featureBackup = new FeatureConfigurationBackup(backupFile, new ModuleLogger(logger, nameof(FeatureConfigurationBackup)));
+ var featureBackup = new FeatureConfigurationBackup(backupFilePath, new ModuleLogger(logger, nameof(FeatureConfigurationBackup)));
var featureFactory = new FeatureConfigurationFactory(new ModuleLogger(logger, nameof(FeatureConfigurationFactory)));
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ProxyFactory)));
var serviceHost = new ServiceHost(AppConfig.SERVICE_ADDRESS, new HostObjectFactory(), new ModuleLogger(logger, nameof(ServiceHost)), FIVE_SECONDS);
@@ -49,7 +50,7 @@ namespace SafeExamBrowser.Service
var bootstrapOperations = new Queue();
var sessionOperations = new Queue();
- sessionContext.AutoRestoreMechanism = new AutoRestoreMechanism(featureBackup);
+ sessionContext.AutoRestoreMechanism = new AutoRestoreMechanism(featureBackup, new ModuleLogger(logger, nameof(AutoRestoreMechanism)), FIVE_SECONDS);
bootstrapOperations.Enqueue(new RestoreOperation(featureBackup, logger, sessionContext));
bootstrapOperations.Enqueue(new CommunicationHostOperation(serviceHost, logger));
diff --git a/SafeExamBrowser.Service/Operations/LockdownOperation.cs b/SafeExamBrowser.Service/Operations/LockdownOperation.cs
index e7bc5808..bbbace0c 100644
--- a/SafeExamBrowser.Service/Operations/LockdownOperation.cs
+++ b/SafeExamBrowser.Service/Operations/LockdownOperation.cs
@@ -34,37 +34,46 @@ namespace SafeExamBrowser.Service.Operations
public override OperationResult Perform()
{
groupId = Guid.NewGuid();
+
+ var success = true;
+ var configurations = new []
+ {
+ (factory.CreateChromeNotificationConfiguration(groupId), Context.Configuration.Settings.Service.DisableChromeNotifications),
+ (factory.CreateEaseOfAccessConfiguration(groupId), Context.Configuration.Settings.Service.DisableEaseOfAccessOptions),
+ (factory.CreateNetworkOptionsConfiguration(groupId), Context.Configuration.Settings.Service.DisableNetworkOptions),
+ (factory.CreatePasswordChangeConfiguration(groupId), Context.Configuration.Settings.Service.DisablePasswordChange),
+ (factory.CreatePowerOptionsConfiguration(groupId), Context.Configuration.Settings.Service.DisablePowerOptions),
+ (factory.CreateRemoteConnectionConfiguration(groupId), Context.Configuration.Settings.Service.DisableRemoteConnections),
+ (factory.CreateSignoutConfiguration(groupId), Context.Configuration.Settings.Service.DisableSignout),
+ (factory.CreateTaskManagerConfiguration(groupId), Context.Configuration.Settings.Service.DisableTaskManager),
+ (factory.CreateUserLockConfiguration(groupId), Context.Configuration.Settings.Service.DisableUserLock),
+ (factory.CreateUserSwitchConfiguration(groupId), Context.Configuration.Settings.Service.DisableUserSwitch),
+ (factory.CreateVmwareOverlayConfiguration(groupId), Context.Configuration.Settings.Service.DisableVmwareOverlay),
+ (factory.CreateWindowsUpdateConfiguration(groupId), Context.Configuration.Settings.Service.DisableWindowsUpdate)
+ };
+
logger.Info($"Attempting to perform lockdown (feature configuration group: {groupId})...");
- var chromeNotification = factory.CreateChromeNotificationConfiguration(groupId);
- var easeOfAccess = factory.CreateEaseOfAccessConfiguration(groupId);
- var networkOptions = factory.CreateNetworkOptionsConfiguration(groupId);
- var passwordChange = factory.CreatePasswordChangeConfiguration(groupId);
- var powerOptions = factory.CreatePowerOptionsConfiguration(groupId);
- var remoteConnection = factory.CreateRemoteConnectionConfiguration(groupId);
- var signout = factory.CreateSignoutConfiguration(groupId);
- var taskManager = factory.CreateTaskManagerConfiguration(groupId);
- var userLock = factory.CreateUserLockConfiguration(groupId);
- var userSwitch = factory.CreateUserSwitchConfiguration(groupId);
- var vmwareOverlay = factory.CreateVmwareOverlayConfiguration(groupId);
- var windowsUpdate = factory.CreateWindowsUpdateConfiguration(groupId);
+ foreach (var (configuration, disable) in configurations)
+ {
+ success &= SetConfiguration(configuration, disable);
- SetConfiguration(chromeNotification, Context.Configuration.Settings.Service.DisableChromeNotifications);
- SetConfiguration(easeOfAccess, Context.Configuration.Settings.Service.DisableEaseOfAccessOptions);
- SetConfiguration(networkOptions, Context.Configuration.Settings.Service.DisableNetworkOptions);
- SetConfiguration(passwordChange, Context.Configuration.Settings.Service.DisablePasswordChange);
- SetConfiguration(powerOptions, Context.Configuration.Settings.Service.DisablePowerOptions);
- SetConfiguration(remoteConnection, Context.Configuration.Settings.Service.DisableRemoteConnections);
- SetConfiguration(signout, Context.Configuration.Settings.Service.DisableSignout);
- SetConfiguration(taskManager, Context.Configuration.Settings.Service.DisableTaskManager);
- SetConfiguration(userLock, Context.Configuration.Settings.Service.DisableUserLock);
- SetConfiguration(userSwitch, Context.Configuration.Settings.Service.DisableUserSwitch);
- SetConfiguration(vmwareOverlay, Context.Configuration.Settings.Service.DisableVmwareOverlay);
- SetConfiguration(windowsUpdate, Context.Configuration.Settings.Service.DisableWindowsUpdate);
+ if (!success)
+ {
+ break;
+ }
+ }
- logger.Info("Lockdown successful.");
+ if (success)
+ {
+ logger.Info("Lockdown successful.");
+ }
+ else
+ {
+ logger.Error("Lockdown was not successful!");
+ }
- return OperationResult.Success;
+ return success ? OperationResult.Success : OperationResult.Failed;
}
public override OperationResult Revert()
@@ -72,32 +81,60 @@ namespace SafeExamBrowser.Service.Operations
logger.Info($"Attempting to revert lockdown (feature configuration group: {groupId})...");
var configurations = backup.GetBy(groupId);
+ var success = true;
foreach (var configuration in configurations)
{
- configuration.Restore();
- backup.Delete(configuration);
+ var restored = configuration.Restore();
+
+ if (restored)
+ {
+ backup.Delete(configuration);
+ }
+ else
+ {
+ logger.Error($"Failed to restore {configuration}!");
+ success = false;
+ }
}
- logger.Info("Lockdown reversion successful.");
+ if (success)
+ {
+ logger.Info("Lockdown reversion successful.");
+ }
+ else
+ {
+ logger.Warn("Lockdown reversion was not successful!");
+ }
- return OperationResult.Success;
+ return success ? OperationResult.Success : OperationResult.Failed;
}
- private void SetConfiguration(IFeatureConfiguration configuration, bool disable)
+ private bool SetConfiguration(IFeatureConfiguration configuration, bool disable)
{
+ var success = false;
+
backup.Save(configuration);
if (disable)
{
- configuration.DisableFeature();
+ success = configuration.DisableFeature();
}
else
{
- configuration.EnableFeature();
+ success = configuration.EnableFeature();
}
- configuration.Monitor();
+ if (success)
+ {
+ configuration.Monitor();
+ }
+ else
+ {
+ logger.Error($"Failed to configure {configuration}!");
+ }
+
+ return success;
}
}
}
diff --git a/SafeExamBrowser.Service/Operations/SessionActivationOperation.cs b/SafeExamBrowser.Service/Operations/SessionActivationOperation.cs
index b2834ace..df39f495 100644
--- a/SafeExamBrowser.Service/Operations/SessionActivationOperation.cs
+++ b/SafeExamBrowser.Service/Operations/SessionActivationOperation.cs
@@ -33,6 +33,8 @@ namespace SafeExamBrowser.Service.Operations
logger.Error("Failed to inform runtime about new session activation!");
}
+ Context.IsRunning = success;
+
return success ? OperationResult.Success : OperationResult.Failed;
}
diff --git a/SafeExamBrowser.Service/Operations/SessionInitializationOperation.cs b/SafeExamBrowser.Service/Operations/SessionInitializationOperation.cs
index 3fe4747e..096c7ae9 100644
--- a/SafeExamBrowser.Service/Operations/SessionInitializationOperation.cs
+++ b/SafeExamBrowser.Service/Operations/SessionInitializationOperation.cs
@@ -61,7 +61,7 @@ namespace SafeExamBrowser.Service.Operations
logger.Info("Finalizing current session...");
- if (Context.ServiceEvent != null)
+ if (Context.ServiceEvent != null && Context.IsRunning)
{
success = Context.ServiceEvent.Set();
@@ -83,6 +83,7 @@ namespace SafeExamBrowser.Service.Operations
logger.Info("Clearing session data...");
Context.Configuration = null;
+ Context.IsRunning = false;
FinalizeSessionWriter();
@@ -107,7 +108,7 @@ namespace SafeExamBrowser.Service.Operations
{
sessionWriter = logWriterFactory.Invoke(Context.Configuration.AppConfig.ServiceLogFilePath);
logger.Subscribe(sessionWriter);
- logger.Debug($"Created session log file {Context.Configuration.AppConfig.ServiceLogFilePath}.");
+ logger.Debug($"Created session log file '{Context.Configuration.AppConfig.ServiceLogFilePath}'.");
}
private void FinalizeSessionWriter()
diff --git a/SafeExamBrowser.Service/ServiceController.cs b/SafeExamBrowser.Service/ServiceController.cs
index 62655d69..21fba95d 100644
--- a/SafeExamBrowser.Service/ServiceController.cs
+++ b/SafeExamBrowser.Service/ServiceController.cs
@@ -31,7 +31,7 @@ namespace SafeExamBrowser.Service
private bool SessionIsRunning
{
- get { return Session != null; }
+ get { return sessionContext.IsRunning; }
}
public ServiceController(
diff --git a/SafeExamBrowser.Service/SessionContext.cs b/SafeExamBrowser.Service/SessionContext.cs
index 33ecb318..16b9d522 100644
--- a/SafeExamBrowser.Service/SessionContext.cs
+++ b/SafeExamBrowser.Service/SessionContext.cs
@@ -27,6 +27,11 @@ namespace SafeExamBrowser.Service
///
internal ServiceConfiguration Configuration { get; set; }
+ ///
+ /// Indicates whether a session is currently running.
+ ///
+ internal bool IsRunning { get; set; }
+
///
/// The global inter-process event used for status synchronization with the runtime component.
///