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:
dbuechel 2019-06-27 08:32:37 +02:00
parent f8111857db
commit dd78bc1fbc
28 changed files with 480 additions and 143 deletions

View file

@ -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();
}
}

View 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)));
}
}
}

View file

@ -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" />

View file

@ -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>

View file

@ -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());
}
}
}

View file

@ -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);
}
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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()
{

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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;
}
}
}

View file

@ -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);
}
}
}

View file

@ -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);
}
}
}

View file

@ -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();

View file

@ -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));

View file

@ -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;
}
}
}

View file

@ -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;
}

View file

@ -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()

View file

@ -31,7 +31,7 @@ namespace SafeExamBrowser.Service
private bool SessionIsRunning
{
get { return Session != null; }
get { return sessionContext.IsRunning; }
}
public ServiceController(

View file

@ -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>