SEBWIN-301: Implemented auto-restore mechanism for service, added return status to feature configuration methods and introduced session flag for service.
This commit is contained in:
parent
f8111857db
commit
dd78bc1fbc
28 changed files with 480 additions and 143 deletions
|
@ -26,14 +26,14 @@ namespace SafeExamBrowser.Contracts.Lockdown
|
|||
Guid GroupId { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Disables the feature.
|
||||
/// Disables the feature. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
void DisableFeature();
|
||||
bool DisableFeature();
|
||||
|
||||
/// <summary>
|
||||
/// Enables the feature.
|
||||
/// Enables the feature. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
void EnableFeature();
|
||||
bool EnableFeature();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the feature to ensure that it remains as currently configured.
|
||||
|
@ -41,8 +41,9 @@ namespace SafeExamBrowser.Contracts.Lockdown
|
|||
void Monitor();
|
||||
|
||||
/// <summary>
|
||||
/// 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 <c>true</c> if successful,
|
||||
/// otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
void Restore();
|
||||
bool Restore();
|
||||
}
|
||||
}
|
||||
|
|
130
SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs
Normal file
130
SafeExamBrowser.Lockdown.UnitTests/AutoRestoreMechanismTests.cs
Normal file
|
@ -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<IFeatureConfigurationBackup> backup;
|
||||
private Mock<ILogger> logger;
|
||||
private AutoRestoreMechanism sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
backup = new Mock<IFeatureConfigurationBackup>();
|
||||
logger = new Mock<ILogger>();
|
||||
|
||||
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<IFeatureConfiguration>();
|
||||
var counter = 0;
|
||||
var limit = new Random().Next(5, 50);
|
||||
var list = new List<IFeatureConfiguration> { configuration.Object };
|
||||
var sync = new AutoResetEvent(false);
|
||||
|
||||
backup.Setup(b => b.GetAllConfigurations()).Returns(list).Callback(() => counter++);
|
||||
backup.Setup(b => b.Delete(It.IsAny<IFeatureConfiguration>())).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<IFeatureConfiguration>(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<IFeatureConfiguration>();
|
||||
var counter = 0;
|
||||
var list = new List<IFeatureConfiguration> { 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<IFeatureConfiguration>();
|
||||
var counter = 0;
|
||||
var list = new List<IFeatureConfiguration> { 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<ArgumentException>(() => new AutoRestoreMechanism(backup.Object, logger.Object, new Random().Next(int.MinValue, -1)));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -57,20 +57,46 @@
|
|||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.4.4.0\lib\net45\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.1.3.2\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.12.0.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.12.0\lib\net45\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.4.5.0\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.5.1\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="AutoRestoreMechanismTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
<None Include="packages.config">
|
||||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Contracts\SafeExamBrowser.Contracts.csproj">
|
||||
<Project>{47da5933-bef8-4729-94e6-abde2db12262}</Project>
|
||||
<Name>SafeExamBrowser.Contracts</Name>
|
||||
</ProjectReference>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Lockdown\SafeExamBrowser.Lockdown.csproj">
|
||||
<Project>{386b6042-3e12-4753-9fc6-c88ea4f97030}</Project>
|
||||
<Name>SafeExamBrowser.Lockdown</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="4.4.0" targetFramework="net472" />
|
||||
<package id="Moq" version="4.12.0" targetFramework="net472" />
|
||||
<package id="MSTest.TestAdapter" version="1.3.2" targetFramework="net472" />
|
||||
<package id="MSTest.TestFramework" version="1.3.2" targetFramework="net472" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="4.5.0" targetFramework="net472" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.5.1" targetFramework="net472" />
|
||||
</packages>
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<IFeatureConfiguration> 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<IFeatureConfiguration> LoadFromFile()
|
||||
{
|
||||
var configurations = new List<IFeatureConfiguration>();
|
||||
|
@ -128,5 +109,32 @@ namespace SafeExamBrowser.Lockdown
|
|||
|
||||
return configurations;
|
||||
}
|
||||
|
||||
private void SaveToFile(List<IFeatureConfiguration> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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<IOperation>();
|
||||
var sessionOperations = new Queue<IOperation>();
|
||||
|
||||
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));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace SafeExamBrowser.Service
|
|||
|
||||
private bool SessionIsRunning
|
||||
{
|
||||
get { return Session != null; }
|
||||
get { return sessionContext.IsRunning; }
|
||||
}
|
||||
|
||||
public ServiceController(
|
||||
|
|
|
@ -27,6 +27,11 @@ namespace SafeExamBrowser.Service
|
|||
/// </summary>
|
||||
internal ServiceConfiguration Configuration { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a session is currently running.
|
||||
/// </summary>
|
||||
internal bool IsRunning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The global inter-process event used for status synchronization with the runtime component.
|
||||
/// </summary>
|
||||
|
|
Loading…
Reference in a new issue