SEBWIN-301, SEBWIN-319: Implemented monitoring mechanism for feature configurations.
This commit is contained in:
parent
6f0b0d0fb2
commit
0d793dd326
17 changed files with 561 additions and 66 deletions
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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>.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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" />
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
148
SafeExamBrowser.Lockdown/FeatureConfigurationMonitor.cs
Normal file
148
SafeExamBrowser.Lockdown/FeatureConfigurationMonitor.cs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
Loading…
Add table
Reference in a new issue