SEBWIN-301, SEBWIN-319: Implemented monitoring mechanism for feature configurations.

This commit is contained in:
dbuechel 2019-07-05 12:28:42 +02:00
parent 6f0b0d0fb2
commit 0d793dd326
17 changed files with 561 additions and 66 deletions

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Contracts.Lockdown
{
/// <summary>
/// Defines all possible states of an <see cref="IFeatureConfiguration"/>.
/// </summary>
public enum FeatureConfigurationStatus
{
/// <summary>
/// The configuration is in an undefined state.
/// </summary>
Undefined,
/// <summary>
/// The configuration is disabled.
/// </summary>
Disabled,
/// <summary>
/// The configuration is enabled.
/// </summary>
Enabled
}
}

View file

@ -35,16 +35,16 @@ namespace SafeExamBrowser.Contracts.Lockdown
/// </summary>
bool EnableFeature();
/// <summary>
/// Retrieves the current status of the configuration.
/// </summary>
FeatureConfigurationStatus GetStatus();
/// <summary>
/// Initializes the currently active configuration of the feature.
/// </summary>
void Initialize();
/// <summary>
/// Starts monitoring the feature to ensure that it remains as currently configured.
/// </summary>
void Monitor();
/// <summary>
/// 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>.

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2019 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
namespace SafeExamBrowser.Contracts.Lockdown
{
/// <summary>
/// Provides functionality to ensure that one or more <see cref="IFeatureConfiguration"/> remain in a given <see cref="FeatureConfigurationStatus"/>.
/// </summary>
public interface IFeatureConfigurationMonitor
{
/// <summary>
/// Registers a configuration to be monitored for the given status.
/// </summary>
void Observe(IFeatureConfiguration configuration, FeatureConfigurationStatus status);
/// <summary>
/// Stops the monitoring activity and removes all observed configurations.
/// </summary>
void Reset();
/// <summary>
/// Starts the monitoring activity.
/// </summary>
void Start();
}
}

View file

@ -92,10 +92,12 @@
<Compile Include="Applications\Events\NameChangedEventHandler.cs" />
<Compile Include="Applications\IApplicationController.cs" />
<Compile Include="Applications\InstanceIdentifier.cs" />
<Compile Include="Lockdown\FeatureConfigurationStatus.cs" />
<Compile Include="Lockdown\IAutoRestoreMechanism.cs" />
<Compile Include="Lockdown\IFeatureConfiguration.cs" />
<Compile Include="Lockdown\IFeatureConfigurationBackup.cs" />
<Compile Include="Lockdown\IFeatureConfigurationFactory.cs" />
<Compile Include="Lockdown\IFeatureConfigurationMonitor.cs" />
<Compile Include="Lockdown\ISystemConfigurationUpdate.cs" />
<Compile Include="Runtime\IRuntimeController.cs" />
<Compile Include="Core\OperationModel\Events\ActionRequiredEventArgs.cs" />

View file

@ -89,7 +89,7 @@ namespace SafeExamBrowser.Lockdown.UnitTests
sync.WaitOne();
sut.Stop();
backup.Verify(b => b.GetAllConfigurations(), Times.Exactly(limit + 1));
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));
systemConfigurationUpdate.Verify(u => u.Execute(), Times.Never);
@ -145,7 +145,7 @@ namespace SafeExamBrowser.Lockdown.UnitTests
Thread.Sleep(25);
sut.Stop();
backup.Verify(b => b.GetAllConfigurations(), Times.Between(counter, counter + 1, Range.Inclusive));
backup.Verify(b => b.GetAllConfigurations(), Times.Exactly(counter));
}
[TestMethod]

View file

@ -0,0 +1,196 @@
/*
* 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.Threading;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown.UnitTests
{
[TestClass]
public class FeatureConfigurationMonitorTests
{
private Mock<ILogger> logger;
private FeatureConfigurationMonitor sut;
[TestInitialize]
public void Initialize()
{
logger = new Mock<ILogger>();
sut = new FeatureConfigurationMonitor(logger.Object, 0);
}
[TestMethod]
public void MustEnforceConfigurations()
{
var configuration1 = new Mock<IFeatureConfiguration>();
var configuration2 = new Mock<IFeatureConfiguration>();
var configuration3 = new Mock<IFeatureConfiguration>();
var counter = 0;
var limit = new Random().Next(5, 50);
var sync = new AutoResetEvent(false);
configuration1.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled);
configuration2.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Enabled);
configuration3.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() =>
{
if (++counter >= limit)
{
sync.Set();
}
});
sut = new FeatureConfigurationMonitor(logger.Object, 2);
sut.Observe(configuration1.Object, FeatureConfigurationStatus.Enabled);
sut.Observe(configuration2.Object, FeatureConfigurationStatus.Disabled);
sut.Observe(configuration3.Object, FeatureConfigurationStatus.Undefined);
sut.Start();
sync.WaitOne();
sut.Reset();
configuration1.Verify(c => c.EnableFeature(), Times.Exactly(limit));
configuration2.Verify(c => c.DisableFeature(), Times.Exactly(limit));
configuration3.Verify(c => c.DisableFeature(), Times.Never);
configuration3.Verify(c => c.EnableFeature(), Times.Never);
}
[TestMethod]
public void MustExecuteAsynchronously()
{
var configuration = new Mock<IFeatureConfiguration>();
var sync = new AutoResetEvent(false);
var threadId = Thread.CurrentThread.ManagedThreadId;
configuration.Setup(c => c.GetStatus()).Callback(() => { threadId = Thread.CurrentThread.ManagedThreadId; sync.Set(); });
sut.Observe(configuration.Object, FeatureConfigurationStatus.Disabled);
sut.Start();
sync.WaitOne();
sut.Reset();
Assert.AreNotEqual(Thread.CurrentThread.ManagedThreadId, threadId);
}
[TestMethod]
public void MustNotStartMultipleTimes()
{
var configuration = new Mock<IFeatureConfiguration>();
var counter = 0;
var sync = new AutoResetEvent(false);
configuration.Setup(c => c.GetStatus()).Returns(() =>
{
counter++;
Thread.Sleep(50);
sync.Set();
sut.Reset();
return FeatureConfigurationStatus.Disabled;
});
sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled);
sut.Start();
sut.Start();
sut.Start();
sut.Start();
sut.Start();
sut.Start();
sut.Start();
sync.WaitOne();
sut.Reset();
Assert.AreEqual(1, counter);
}
[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 sync = new AutoResetEvent(false);
sut = new FeatureConfigurationMonitor(logger.Object, TIMEOUT);
configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Undefined).Callback(() =>
{
switch (++counter)
{
case 1:
before = DateTime.Now;
break;
case 2:
after = DateTime.Now;
sync.Set();
break;
}
});
sut.Observe(configuration.Object, FeatureConfigurationStatus.Disabled);
sut.Start();
sync.WaitOne();
sut.Reset();
Assert.IsTrue(after - before >= new TimeSpan(0, 0, 0, 0, TIMEOUT));
}
[TestMethod]
public void MustStopWhenReset()
{
var configuration = new Mock<IFeatureConfiguration>();
var counter = 0;
configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() => counter++);
sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled);
sut.Start();
Thread.Sleep(10);
sut.Reset();
Thread.Sleep(10);
configuration.Verify(c => c.GetStatus(), Times.Exactly(counter));
}
[TestMethod]
public void MustRemoveConfigurationsWhenReset()
{
var configuration = new Mock<IFeatureConfiguration>();
var counter = 0;
configuration.Setup(c => c.GetStatus()).Returns(FeatureConfigurationStatus.Disabled).Callback(() => counter++);
sut.Observe(configuration.Object, FeatureConfigurationStatus.Enabled);
sut.Start();
Thread.Sleep(10);
sut.Reset();
configuration.Verify(c => c.GetStatus(), Times.Exactly(counter));
configuration.Reset();
sut.Start();
Thread.Sleep(10);
sut.Reset();
configuration.Verify(c => c.GetStatus(), Times.Never);
}
[TestMethod]
[ExpectedException(typeof(ArgumentException))]
public void MustValidateTimeout()
{
new FeatureConfigurationMonitor(logger.Object, new Random().Next(int.MinValue, -1));
}
}
}

View file

@ -32,12 +32,12 @@ namespace SafeExamBrowser.Lockdown.UnitTests
throw new NotImplementedException();
}
public void Initialize()
public FeatureConfigurationStatus GetStatus()
{
throw new NotImplementedException();
}
public void Monitor()
public void Initialize()
{
throw new NotImplementedException();
}

View file

@ -81,6 +81,7 @@
</ItemGroup>
<ItemGroup>
<Compile Include="FeatureConfigurationBackupTests.cs" />
<Compile Include="FeatureConfigurationMonitorTests.cs" />
<Compile Include="FeatureConfigurationStub.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="AutoRestoreMechanismTests.cs" />

View file

@ -7,6 +7,7 @@
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using SafeExamBrowser.Contracts.Lockdown;
@ -48,7 +49,7 @@ namespace SafeExamBrowser.Lockdown
if (!running)
{
running = true;
Task.Run(new Action(RestoreAll));
Task.Run(new Action(AutoRestore));
logger.Info("Started auto-restore mechanism.");
}
else
@ -74,47 +75,31 @@ namespace SafeExamBrowser.Lockdown
}
}
private void RestoreAll()
private void AutoRestore()
{
if (IsStopped())
{
return;
}
var configurations = backup.GetAllConfigurations();
var all = configurations.Count;
var restored = 0;
if (configurations.Any())
{
logger.Info($"Attempting to restore {configurations.Count} items...");
var success = TryRestoreAll(configurations);
foreach (var configuration in configurations)
{
var success = configuration.Restore();
if (success)
{
backup.Delete(configuration);
restored++;
}
else
{
logger.Warn($"Failed to restore {configuration}!");
}
lock (@lock)
{
if (!running)
{
logger.Info("Auto-restore mechanism was aborted.");
return;
}
}
}
if (all == restored)
if (success == true)
{
systemConfigurationUpdate.ExecuteAsync();
}
Task.Delay(timeout_ms).ContinueWith((_) => RestoreAll());
else if (success == false)
{
Task.Delay(timeout_ms).ContinueWith((_) => AutoRestore());
}
else
{
logger.Info("Auto-restore mechanism was aborted.");
}
}
else
{
@ -125,5 +110,55 @@ namespace SafeExamBrowser.Lockdown
}
}
}
private bool IsStopped()
{
lock (@lock)
{
return !running;
}
}
private bool? TryRestoreAll(IList<IFeatureConfiguration> configurations)
{
var restored = 0;
logger.Info($"Attempting to restore {configurations.Count} items...");
foreach (var configuration in configurations)
{
var success = TryRestore(configuration);
if (success)
{
restored++;
}
if (IsStopped())
{
return null;
}
}
logger.Info($"Restored {restored} of {configurations.Count} items.");
return restored == configurations.Count;
}
private bool TryRestore(IFeatureConfiguration configuration)
{
var success = configuration.Restore();
if (success)
{
backup.Delete(configuration);
}
else
{
logger.Warn($"Failed to restore {configuration}!");
}
return success;
}
}
}

View file

@ -0,0 +1,148 @@
/*
* 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.Linq;
using System.Threading.Tasks;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown
{
public class FeatureConfigurationMonitor : IFeatureConfigurationMonitor
{
private readonly object @lock = new object();
private IDictionary<IFeatureConfiguration, FeatureConfigurationStatus> configurations;
private ILogger logger;
private readonly int timeout_ms;
private bool running;
public FeatureConfigurationMonitor(ILogger logger, int timeout_ms)
{
if (timeout_ms < 0)
{
throw new ArgumentException("Must be 0 or greater!", nameof(timeout_ms));
}
this.configurations = new Dictionary<IFeatureConfiguration, FeatureConfigurationStatus>();
this.logger = logger;
this.timeout_ms = timeout_ms;
}
public void Observe(IFeatureConfiguration configuration, FeatureConfigurationStatus status)
{
lock (@lock)
{
configurations.Add(configuration, status);
}
}
public void Reset()
{
lock (@lock)
{
if (running)
{
running = false;
configurations.Clear();
logger.Info("Stopped monitoring.");
}
else
{
logger.Info("Monitoring is not running.");
}
}
}
public void Start()
{
lock (@lock)
{
if (!running)
{
running = true;
Task.Run(new Action(Monitor));
logger.Info("Started monitoring.");
}
else
{
logger.Info("Monitoring is already running.");
}
}
}
private void Monitor()
{
if (IsStopped())
{
return;
}
var configurations = new Dictionary<IFeatureConfiguration, FeatureConfigurationStatus>(this.configurations);
logger.Debug($"Checking {configurations.Count} configurations...");
foreach (var item in configurations)
{
var configuration = item.Key;
var status = item.Value;
Enforce(configuration, status);
if (IsStopped())
{
logger.Info("Monitoring was aborted.");
return;
}
}
if (configurations.Any())
{
Task.Delay(timeout_ms).ContinueWith((_) => Monitor());
}
else
{
lock (@lock)
{
running = false;
logger.Info("Nothing to be monitored, stopped monitoring.");
}
}
}
private void Enforce(IFeatureConfiguration configuration, FeatureConfigurationStatus status)
{
var currentStatus = configuration.GetStatus();
if (currentStatus != status)
{
logger.Warn($"{configuration} is {currentStatus.ToString().ToLower()} instead of {status.ToString().ToLower()}!");
if (status == FeatureConfigurationStatus.Disabled)
{
configuration.DisableFeature();
}
else if (status == FeatureConfigurationStatus.Enabled)
{
configuration.EnableFeature();
}
}
}
private bool IsStopped()
{
lock (@lock)
{
return !running;
}
}
}
}

View file

@ -31,8 +31,8 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
public abstract bool DisableFeature();
public abstract bool EnableFeature();
public abstract FeatureConfigurationStatus GetStatus();
public abstract void Initialize();
public abstract void Monitor();
public abstract bool Restore();
public override string ToString()

View file

@ -10,6 +10,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations
@ -88,9 +89,31 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations
}
}
public override void Monitor()
public override FeatureConfigurationStatus GetStatus()
{
// TODO!
var status = FeatureConfigurationStatus.Undefined;
foreach (var item in Items)
{
var current = ReadItem(item.Key, item.Value);
if (current.Data?.Equals(item.Disabled) == true && status != FeatureConfigurationStatus.Enabled)
{
status = FeatureConfigurationStatus.Disabled;
}
else if (current.Data?.Equals(item.Enabled) == true && status != FeatureConfigurationStatus.Disabled)
{
status = FeatureConfigurationStatus.Enabled;
}
else
{
status = FeatureConfigurationStatus.Undefined;
break;
}
}
return status;
}
public override bool Restore()

View file

@ -7,6 +7,7 @@
*/
using System;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Lockdown.FeatureConfigurations
@ -28,12 +29,12 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations
return true;
}
public override void Initialize()
public override FeatureConfigurationStatus GetStatus()
{
return FeatureConfigurationStatus.Undefined;
}
public override void Monitor()
public override void Initialize()
{
}

View file

@ -56,6 +56,7 @@
<Compile Include="AutoRestoreMechanism.cs" />
<Compile Include="FeatureConfigurationFactory.cs" />
<Compile Include="FeatureConfigurationBackup.cs" />
<Compile Include="FeatureConfigurationMonitor.cs" />
<Compile Include="FeatureConfigurations\RegistryConfigurations\RegistryDataItem.cs" />
<Compile Include="FeatureConfigurations\RegistryConfigurations\RegistryConfigurationItem.cs" />
<Compile Include="FeatureConfigurations\RegistryConfigurations\UserHive\ChromeNotificationConfiguration.cs" />

View file

@ -25,6 +25,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
{
private Mock<IFeatureConfigurationBackup> backup;
private Mock<IFeatureConfigurationFactory> factory;
private Mock<IFeatureConfigurationMonitor> monitor;
private Mock<ILogger> logger;
private Settings settings;
private SessionContext sessionContext;
@ -35,6 +36,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
{
backup = new Mock<IFeatureConfigurationBackup>();
factory = new Mock<IFeatureConfigurationFactory>();
monitor = new Mock<IFeatureConfigurationMonitor>();
logger = new Mock<ILogger>();
settings = new Settings();
sessionContext = new SessionContext
@ -42,7 +44,7 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
Configuration = new ServiceConfiguration { Settings = settings, UserName = "TestName", UserSid = "S-1-234-TEST" }
};
sut = new LockdownOperation(backup.Object, factory.Object, logger.Object, sessionContext);
sut = new LockdownOperation(backup.Object, factory.Object, monitor.Object, logger.Object, sessionContext);
}
[TestMethod]
@ -63,7 +65,9 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
configuration.Verify(c => c.Initialize(), Times.Exactly(count));
configuration.Verify(c => c.DisableFeature(), Times.Exactly(3));
configuration.Verify(c => c.EnableFeature(), Times.Exactly(count - 3));
configuration.Verify(c => c.Monitor(), Times.Exactly(count));
monitor.Verify(m => m.Observe(It.Is<IFeatureConfiguration>(c => c == configuration.Object), It.Is<FeatureConfigurationStatus>(s => s == FeatureConfigurationStatus.Disabled)), Times.Exactly(3));
monitor.Verify(m => m.Observe(It.Is<IFeatureConfiguration>(c => c == configuration.Object), It.Is<FeatureConfigurationStatus>(s => s == FeatureConfigurationStatus.Enabled)), Times.Exactly(count - 3));
monitor.Verify(m => m.Start(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
@ -114,7 +118,8 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
configuration.Verify(c => c.Initialize(), Times.Exactly(count - offset));
configuration.Verify(c => c.DisableFeature(), Times.Never);
configuration.Verify(c => c.EnableFeature(), Times.Exactly(count - offset));
configuration.Verify(c => c.Monitor(), Times.Exactly(count - offset - 1));
monitor.Verify(m => m.Observe(It.Is<IFeatureConfiguration>(c => c == configuration.Object), It.Is<FeatureConfigurationStatus>(s => s == FeatureConfigurationStatus.Enabled)), Times.Exactly(count - offset - 1));
monitor.Verify(m => m.Start(), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
@ -167,6 +172,8 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
configuration4.Verify(c => c.Initialize(), Times.Never);
configuration4.Verify(c => c.Restore(), Times.Once);
monitor.Verify(m => m.Reset(), Times.Once);
Assert.AreEqual(OperationResult.Success, result);
}
@ -218,6 +225,8 @@ namespace SafeExamBrowser.Service.UnitTests.Operations
configuration4.Verify(c => c.Initialize(), Times.Never);
configuration4.Verify(c => c.Restore(), Times.Once);
monitor.Verify(m => m.Reset(), Times.Once);
Assert.AreEqual(OperationResult.Failed, result);
}
}

View file

@ -35,6 +35,7 @@ namespace SafeExamBrowser.Service
internal void BuildObjectGraph()
{
const int ONE_SECOND = 1000;
const int FIVE_SECONDS = 5000;
var backupFilePath = BuildBackupFilePath();
@ -43,6 +44,7 @@ namespace SafeExamBrowser.Service
var featureBackup = new FeatureConfigurationBackup(backupFilePath, new ModuleLogger(logger, nameof(FeatureConfigurationBackup)));
var featureFactory = new FeatureConfigurationFactory(new ModuleLogger(logger, nameof(FeatureConfigurationFactory)));
var featureMonitor = new FeatureConfigurationMonitor(new ModuleLogger(logger, nameof(FeatureConfigurationMonitor)), ONE_SECOND);
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);
var sessionContext = new SessionContext();
@ -58,7 +60,7 @@ namespace SafeExamBrowser.Service
bootstrapOperations.Enqueue(new ServiceEventCleanupOperation(logger, sessionContext));
sessionOperations.Enqueue(new SessionInitializationOperation(logger, ServiceEventFactory, sessionContext));
sessionOperations.Enqueue(new LockdownOperation(featureBackup, featureFactory, logger, sessionContext));
sessionOperations.Enqueue(new LockdownOperation(featureBackup, featureFactory, featureMonitor, logger, sessionContext));
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
var bootstrapSequence = new OperationSequence(logger, bootstrapOperations);

View file

@ -17,17 +17,20 @@ namespace SafeExamBrowser.Service.Operations
{
private IFeatureConfigurationBackup backup;
private IFeatureConfigurationFactory factory;
private IFeatureConfigurationMonitor monitor;
private ILogger logger;
private Guid groupId;
public LockdownOperation(
IFeatureConfigurationBackup backup,
IFeatureConfigurationFactory factory,
IFeatureConfigurationMonitor monitor,
ILogger logger,
SessionContext sessionContext) : base(sessionContext)
{
this.backup = backup;
this.factory = factory;
this.monitor = monitor;
this.logger = logger;
}
@ -58,7 +61,7 @@ namespace SafeExamBrowser.Service.Operations
foreach (var (configuration, disable) in configurations)
{
success &= SetConfiguration(configuration, disable);
success &= TrySet(configuration, disable);
if (!success)
{
@ -68,6 +71,7 @@ namespace SafeExamBrowser.Service.Operations
if (success)
{
monitor.Start();
logger.Info("Lockdown successful.");
}
else
@ -85,19 +89,11 @@ namespace SafeExamBrowser.Service.Operations
var configurations = backup.GetBy(groupId);
var success = true;
monitor.Reset();
foreach (var configuration in configurations)
{
var restored = configuration.Restore();
if (restored)
{
backup.Delete(configuration);
}
else
{
logger.Error($"Failed to restore {configuration}!");
success = false;
}
success &= TryRestore(configuration);
}
if (success)
@ -112,9 +108,26 @@ namespace SafeExamBrowser.Service.Operations
return success ? OperationResult.Success : OperationResult.Failed;
}
private bool SetConfiguration(IFeatureConfiguration configuration, bool disable)
private bool TryRestore(IFeatureConfiguration configuration)
{
var success = configuration.Restore();
if (success)
{
backup.Delete(configuration);
}
else
{
logger.Error($"Failed to restore {configuration}!");
}
return success;
}
private bool TrySet(IFeatureConfiguration configuration, bool disable)
{
var success = false;
var status = FeatureConfigurationStatus.Undefined;
configuration.Initialize();
backup.Save(configuration);
@ -122,15 +135,17 @@ namespace SafeExamBrowser.Service.Operations
if (disable)
{
success = configuration.DisableFeature();
status = FeatureConfigurationStatus.Disabled;
}
else
{
success = configuration.EnableFeature();
status = FeatureConfigurationStatus.Enabled;
}
if (success)
{
configuration.Monitor();
monitor.Observe(configuration, status);
}
else
{