SEBWIN-221: Found better solution for binary format (separated encryption code) and implemented scaffolding for XML parsing.

This commit is contained in:
dbuechel 2018-11-28 15:43:30 +01:00
parent 7f38c0b8c3
commit 243d32879e
18 changed files with 700 additions and 454 deletions

View file

@ -124,7 +124,7 @@ namespace SafeExamBrowser.Configuration
resourceLoaders.Add(resourceLoader);
}
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string password = null)
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string password = null, bool passwordIsHash = false)
{
settings = default(Settings);
@ -141,7 +141,7 @@ namespace SafeExamBrowser.Configuration
case LoadStatus.LoadWithBrowser:
return HandleBrowserResource(resource, out settings);
case LoadStatus.Success:
return TryParseData(data, out settings, password);
return TryParseData(data, out settings, password, passwordIsHash);
}
}
@ -175,7 +175,7 @@ namespace SafeExamBrowser.Configuration
return status;
}
private LoadStatus TryParseData(Stream data, out Settings settings, string password)
private LoadStatus TryParseData(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
{
var status = LoadStatus.NotSupported;
var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data));
@ -184,7 +184,7 @@ namespace SafeExamBrowser.Configuration
if (dataFormat != null)
{
status = dataFormat.TryParse(data, out settings, password);
status = dataFormat.TryParse(data, out settings, password, passwordIsHash);
logger.Info($"Tried to parse data from '{data}' using {dataFormat.GetType().Name} -> Result: {status}.");
}
else

View file

@ -1,48 +0,0 @@
/*
* Copyright (c) 2018 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.IO;
using System.Text;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration.DataFormats
{
public partial class BinaryFormat
{
private LoadStatus ParsePlainData(Stream data, out Settings settings)
{
if (compressor.IsCompressed(data))
{
data = compressor.Decompress(data);
}
var buffer = new byte[4096];
var bytesRead = 0;
var xmlBuilder = new StringBuilder();
data.Seek(0, SeekOrigin.Begin);
do
{
bytesRead = data.Read(buffer, 0, buffer.Length);
xmlBuilder.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
} while (bytesRead > 0);
var xml = xmlBuilder.ToString();
// TODO: Parse XML data...
settings = new Settings();
settings.Browser.AllowAddressBar = true;
settings.Browser.AllowConfigurationDownloads = true;
return LoadStatus.Success;
}
}
}

View file

@ -9,6 +9,7 @@
using System;
using System.IO;
using System.Text;
using SafeExamBrowser.Configuration.DataFormats.Cryptography;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging;
@ -20,11 +21,13 @@ namespace SafeExamBrowser.Configuration.DataFormats
private const int PREFIX_LENGTH = 4;
private IDataCompressor compressor;
private ILogger logger;
private IHashAlgorithm hashAlgorithm;
private IModuleLogger logger;
public BinaryFormat(IDataCompressor compressor, ILogger logger)
public BinaryFormat(IDataCompressor compressor, IHashAlgorithm hashAlgorithm, IModuleLogger logger)
{
this.compressor = compressor;
this.hashAlgorithm = hashAlgorithm;
this.logger = logger;
}
@ -54,7 +57,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
return false;
}
public LoadStatus TryParse(Stream data, out Settings settings, string password = null)
public LoadStatus TryParse(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
{
var prefix = ParsePrefix(data);
var success = TryDetermineFormat(prefix, out FormatType format);
@ -69,19 +72,19 @@ namespace SafeExamBrowser.Configuration.DataFormats
}
data = new SubStream(data, PREFIX_LENGTH, data.Length - PREFIX_LENGTH);
logger.Debug($"Attempting to parse '{data}' with format '{prefix}'...");
// TODO: Try to abstract (Parser -> Binary, Xml, ...; DataBlock -> Password, PlainData, ...) once fully implemented!
switch (format)
{
case FormatType.Password:
case FormatType.PasswordConfigureClient:
return ParsePassword(data, format, out settings, password);
return ParsePasswordBlock(data, format, out settings, password, passwordIsHash);
case FormatType.PlainData:
return ParsePlainData(data, out settings);
return ParsePlainDataBlock(data, out settings);
case FormatType.PublicKeyHash:
return ParsePublicKeyHash(data, out settings, password);
case FormatType.PublicKeyHashSymmetricKey:
return ParsePublicKeyHashWithSymmetricKey(data, out settings, password);
return ParsePublicKeyHashBlock(data, out settings, password, passwordIsHash);
case FormatType.PublicKeyHashWithSymmetricKey:
return ParsePublicKeyHashWithSymmetricKeyBlock(data, out settings, password, passwordIsHash);
}
}
@ -90,6 +93,50 @@ namespace SafeExamBrowser.Configuration.DataFormats
return LoadStatus.InvalidData;
}
private LoadStatus ParsePasswordBlock(Stream data, FormatType format, out Settings settings, string password, bool passwordIsHash)
{
var encryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption)));
settings = default(Settings);
// TODO: Check whether the hashing (bool passwordIsHash) can be extracted and moved to ConfigurationOperation!
if (format == FormatType.PasswordConfigureClient && !passwordIsHash)
{
password = hashAlgorithm.GenerateHashFor(password);
}
var status = encryption.Decrypt(data, out Stream decrypted, password);
if (status == LoadStatus.Success)
{
return ParsePlainDataBlock(decrypted, out settings);
}
return status;
}
private LoadStatus ParsePlainDataBlock(Stream data, out Settings settings)
{
var xmlFormat = new XmlFormat(logger.CloneFor(nameof(XmlFormat)));
if (compressor.IsCompressed(data))
{
data = compressor.Decompress(data);
}
return xmlFormat.TryParse(data, out settings);
}
private LoadStatus ParsePublicKeyHashBlock(Stream data, out Settings settings, string password, bool passwordIsHash)
{
throw new NotImplementedException();
}
private LoadStatus ParsePublicKeyHashWithSymmetricKeyBlock(Stream data, out Settings settings, string password, bool passwordIsHash)
{
throw new NotImplementedException();
}
private string ParsePrefix(Stream data)
{
var prefixData = new byte[PREFIX_LENGTH];
@ -126,7 +173,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
format = FormatType.PublicKeyHash;
return true;
case "phsk":
format = FormatType.PublicKeyHashSymmetricKey;
format = FormatType.PublicKeyHashWithSymmetricKey;
return true;
}
@ -139,7 +186,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
PasswordConfigureClient,
PlainData,
PublicKeyHash,
PublicKeyHashSymmetricKey
PublicKeyHashWithSymmetricKey
}
}
}

View file

@ -10,13 +10,12 @@ using System;
using System.IO;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.Configuration.DataFormats
namespace SafeExamBrowser.Configuration.DataFormats.Cryptography
{
public partial class BinaryFormat
internal class PasswordEncryption
{
private const int BLOCK_SIZE = 16;
private const int HEADER_SIZE = 2;
@ -26,9 +25,21 @@ namespace SafeExamBrowser.Configuration.DataFormats
private const int SALT_SIZE = 8;
private const int VERSION = 0x2;
private LoadStatus ParsePassword(Stream data, FormatType format, out Settings settings, string password = null)
private ILogger logger;
internal PasswordEncryption(ILogger logger)
{
settings = default(Settings);
this.logger = logger;
}
internal Stream Encrypt(Stream data, string password)
{
throw new NotImplementedException();
}
internal LoadStatus Decrypt(Stream data, out Stream decrypted, string password)
{
decrypted = default(Stream);
if (password == null)
{
@ -39,13 +50,9 @@ namespace SafeExamBrowser.Configuration.DataFormats
if (version != VERSION || options != OPTIONS)
{
return FailForInvalidPasswordHeader(version, options);
}
logger.Error($"Invalid encryption header! Expected: [{VERSION},{OPTIONS},...] - Actual: [{version},{options},...]");
if (format == FormatType.PasswordConfigureClient)
{
// TODO: Shouldn't this not only be done for admin password, and not settings password?!?
password = GeneratePasswordHash(password);
return LoadStatus.InvalidData;
}
var (authenticationKey, encryptionKey) = GenerateKeys(data, password);
@ -53,13 +60,14 @@ namespace SafeExamBrowser.Configuration.DataFormats
if (!computedHmac.SequenceEqual(originalHmac))
{
return FailForInvalidPasswordHmac();
logger.Warn($"The authentication failed due to an invalid password or corrupted data!");
return LoadStatus.PasswordNeeded;
}
using (var plainData = Decrypt(data, encryptionKey, originalHmac.Length))
{
return ParsePlainData(plainData, out settings);
}
decrypted = Decrypt(data, encryptionKey, originalHmac.Length);
return LoadStatus.Success;
}
private (int version, int options) ParseHeader(Stream data)
@ -72,25 +80,6 @@ namespace SafeExamBrowser.Configuration.DataFormats
return (version, options);
}
private LoadStatus FailForInvalidPasswordHeader(int version, int options)
{
logger.Error($"Invalid encryption header! Expected: [{VERSION},{OPTIONS},...] - Actual: [{version},{options},...]");
return LoadStatus.InvalidData;
}
private string GeneratePasswordHash(string input)
{
using (var algorithm = new SHA256Managed())
{
var bytes = Encoding.UTF8.GetBytes(input);
var hash = algorithm.ComputeHash(bytes);
var @string = String.Join(String.Empty, hash.Select(b => b.ToString("x2")));
return @string;
}
}
private (byte[] authenticationKey, byte[] encryptionKey) GenerateKeys(Stream data, string password)
{
var authenticationSalt = new byte[SALT_SIZE];
@ -125,13 +114,6 @@ namespace SafeExamBrowser.Configuration.DataFormats
}
}
private LoadStatus FailForInvalidPasswordHmac()
{
logger.Warn($"The authentication failed due to an invalid password or corrupted data!");
return LoadStatus.PasswordNeeded;
}
private Stream Decrypt(Stream data, byte[] encryptionKey, int hmacLength)
{
var initializationVector = new byte[BLOCK_SIZE];

View file

@ -8,14 +8,17 @@
using System;
using System.IO;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration.DataFormats
namespace SafeExamBrowser.Configuration.DataFormats.Cryptography
{
public partial class BinaryFormat
internal class PublicKeyHashEncryption
{
private LoadStatus ParsePublicKeyHash(Stream data, out Settings settings, string password)
internal Stream Encrypt(Stream data, string password)
{
throw new NotImplementedException();
}
internal Stream Decrypt(Stream data, string password)
{
throw new NotImplementedException();
}

View file

@ -8,14 +8,17 @@
using System;
using System.IO;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
namespace SafeExamBrowser.Configuration.DataFormats
namespace SafeExamBrowser.Configuration.DataFormats.Cryptography
{
public partial class BinaryFormat
internal class PublicKeyHashWithSymmetricKeyEncryption
{
private LoadStatus ParsePublicKeyHashWithSymmetricKey(Stream data, out Settings settings, string password)
internal Stream Encrypt(Stream data, string password)
{
throw new NotImplementedException();
}
internal Stream Decrypt(Stream data, string password)
{
throw new NotImplementedException();
}

View file

@ -6,7 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.IO;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging;
@ -15,6 +19,12 @@ namespace SafeExamBrowser.Configuration.DataFormats
{
public class XmlFormat : IDataFormat
{
private const string ARRAY = "array";
private const string DICTIONARY = "dict";
private const string KEY = "key";
private const string ROOT_NODE = "plist";
private const string XML_PREFIX = "<?xm";
private ILogger logger;
public XmlFormat(ILogger logger)
@ -24,12 +34,178 @@ namespace SafeExamBrowser.Configuration.DataFormats
public bool CanParse(Stream data)
{
try
{
var prefixData = new byte[XML_PREFIX.Length];
var longEnough = data.Length > prefixData.Length;
if (longEnough)
{
data.Seek(0, SeekOrigin.Begin);
data.Read(prefixData, 0, prefixData.Length);
var prefix = Encoding.UTF8.GetString(prefixData);
var success = prefix == XML_PREFIX;
logger.Debug($"'{data}' starting with '{prefix}' does {(success ? string.Empty : "not ")}match the XML format.");
return success;
}
logger.Debug($"'{data}' is not long enough ({data.Length} bytes) to match the XML format.");
}
catch (Exception e)
{
logger.Error($"Failed to determine whether '{data}' with {data.Length / 1000.0} KB data matches the XML format!", e);
}
return false;
}
public LoadStatus TryParse(Stream data, out Settings settings, string password = null)
public LoadStatus TryParse(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
{
throw new System.NotImplementedException();
var status = LoadStatus.InvalidData;
settings = new Settings();
data.Seek(0, SeekOrigin.Begin);
using (var reader = XmlReader.Create(data, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
{
var hasRoot = reader.ReadToFollowing(ROOT_NODE);
if (hasRoot)
{
logger.Debug($"Found root node, starting to parse data...");
Parse(reader, settings, out status);
logger.Debug($"Finished parsing -> Result: {status}.");
}
else
{
logger.Error($"Could not find root node '{ROOT_NODE}'!");
}
}
return status;
}
private void Parse(XmlReader reader, Settings settings, out LoadStatus status)
{
status = LoadStatus.Success;
if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case ARRAY:
ParseArray(reader, settings, out status);
break;
case DICTIONARY:
ParseDictionary(reader, settings, out status);
break;
case KEY:
ParseKeyValuePair(reader, settings, out status);
break;
case ROOT_NODE:
break;
default:
status = LoadStatus.InvalidData;
logger.Error($"Detected invalid element '{reader.Name}'!");
break;
}
}
reader.Read();
reader.MoveToContent();
if (!reader.EOF && status == LoadStatus.Success)
{
Parse(reader, settings, out status);
}
}
private void ParseArray(XmlReader reader, Settings settings, out LoadStatus status)
{
reader.Read();
reader.MoveToContent();
while (reader.NodeType == XmlNodeType.Element)
{
if (reader.Name == ARRAY)
{
ParseArray(reader, settings, out status);
}
else if (reader.Name == DICTIONARY)
{
ParseDictionary(reader, settings, out status);
}
else
{
var item = XNode.ReadFrom(reader) as XElement;
// TODO: Map data...
}
reader.Read();
reader.MoveToContent();
}
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == ARRAY)
{
status = LoadStatus.Success;
}
else
{
logger.Error($"Expected closing tag for '{ARRAY}', but found '{reader.Name}{reader.Value}'!");
status = LoadStatus.InvalidData;
}
}
private void ParseDictionary(XmlReader reader, Settings settings, out LoadStatus status)
{
reader.Read();
reader.MoveToContent();
while (reader.NodeType == XmlNodeType.Element)
{
ParseKeyValuePair(reader, settings, out status);
reader.Read();
reader.MoveToContent();
}
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == DICTIONARY)
{
status = LoadStatus.Success;
}
else
{
logger.Error($"Expected closing tag for '{DICTIONARY}', but found '{reader.Name}{reader.Value}'!");
status = LoadStatus.InvalidData;
}
}
private void ParseKeyValuePair(XmlReader reader, Settings settings, out LoadStatus status)
{
var key = XNode.ReadFrom(reader) as XElement;
reader.Read();
reader.MoveToContent();
if (reader.Name == ARRAY)
{
ParseArray(reader, settings, out status);
}
else if (reader.Name == DICTIONARY)
{
ParseDictionary(reader, settings, out status);
}
else
{
var value = XNode.ReadFrom(reader) as XElement;
// TODO: Map data...
status = LoadStatus.Success;
}
}
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2018 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.Linq;
using System.Security.Cryptography;
using System.Text;
using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.Configuration
{
public class HashAlgorithm : IHashAlgorithm
{
public string GenerateHashFor(string password)
{
using (var algorithm = new SHA256Managed())
{
var bytes = Encoding.UTF8.GetBytes(password);
var hash = algorithm.ComputeHash(bytes);
var hashString = String.Join(String.Empty, hash.Select(b => b.ToString("x2")));
return hashString;
}
}
}
}

View file

@ -54,23 +54,17 @@
</Reference>
<Reference Include="System.Windows.Forms" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.XML" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
<Compile Include="Compression\GZipCompressor.cs" />
<Compile Include="DataFormats\BinaryFormat.cs" />
<Compile Include="DataFormats\BinaryFormat.Password.cs">
<DependentUpon>BinaryFormat.cs</DependentUpon>
</Compile>
<Compile Include="DataFormats\BinaryFormat.PlainData.cs">
<DependentUpon>BinaryFormat.cs</DependentUpon>
</Compile>
<Compile Include="DataFormats\BinaryFormat.PublicKeyHash.cs">
<DependentUpon>BinaryFormat.cs</DependentUpon>
</Compile>
<Compile Include="DataFormats\BinaryFormat.PublicKeyHashWithSymmetricKey.cs">
<DependentUpon>BinaryFormat.cs</DependentUpon>
</Compile>
<Compile Include="DataFormats\Cryptography\PasswordEncryption.cs" />
<Compile Include="DataFormats\Cryptography\PublicKeyHashEncryption.cs" />
<Compile Include="DataFormats\Cryptography\PublicKeyHashWithSymmetricKeyEncryption.cs" />
<Compile Include="DataFormats\XmlFormat.cs" />
<Compile Include="HashAlgorithm.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ConfigurationRepository.cs" />
<Compile Include="ResourceLoaders\FileResourceLoader.cs" />

View file

@ -44,6 +44,6 @@ namespace SafeExamBrowser.Contracts.Configuration
/// Attempts to load settings from the specified resource, using the optional password. As long as the result is not
/// <see cref="LoadStatus.Success"/>, the referenced settings may be <c>null</c> or in an undefinable state!
/// </summary>
LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string password = null);
LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string password = null, bool passwordIsHash = false);
}
}

View file

@ -24,6 +24,6 @@ namespace SafeExamBrowser.Contracts.Configuration
/// Tries to parse the given data, using the optional password. As long as the result is not <see cref="LoadStatus.Success"/>,
/// the referenced settings may be <c>null</c> or in an undefinable state!
/// </summary>
LoadStatus TryParse(Stream data, out Settings.Settings settings, string password = null);
LoadStatus TryParse(Stream data, out Settings.Settings settings, string password = null, bool passwordIsHash = false);
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2018 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.Configuration
{
/// <summary>
/// Provides functionality to calculate hash codes of different objects.
/// </summary>
public interface IHashAlgorithm
{
/// <summary>
/// Computes a hash code for the given password.
/// </summary>
string GenerateHashFor(string password);
}
}

View file

@ -16,6 +16,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable]
public class Settings
{
/// <summary>
/// The hash code of the administrator password for the settings.
/// </summary>
public string AdminPasswordHash { get; set; }
/// <summary>
/// The mode which determines the configuration behaviour.
/// </summary>

View file

@ -55,6 +55,7 @@
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
<Compile Include="Configuration\IDataCompressor.cs" />
<Compile Include="Configuration\IDataFormat.cs" />
<Compile Include="Configuration\IHashAlgorithm.cs" />
<Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" />
<Compile Include="Core\Events\NameChangedEventHandler.cs" />
<Compile Include="Core\IApplicationController.cs" />

View file

@ -51,367 +51,373 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
}
[TestMethod]
public void MustUseCommandLineArgumentAs1stPrio()
public void TODO()
{
var settings = default(Settings);
var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
appConfig.ProgramDataFolder = location;
appConfig.AppDataFolder = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.Perform();
var resource = new Uri(url);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
Assert.Fail();
}
[TestMethod]
public void MustUseProgramDataAs2ndPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
//[TestMethod]
//public void MustUseCommandLineArgumentAs1stPrio()
//{
// var settings = default(Settings);
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
appConfig.ProgramDataFolder = location;
appConfig.AppDataFolder = $@"{location}\WRONG";
// appConfig.ProgramDataFolder = location;
// appConfig.AppDataFolder = location;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.Perform();
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.Perform();
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
// var resource = new Uri(url);
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
}
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
//}
[TestMethod]
public void MustUseAppDataAs3rdPrio()
{
var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
//[TestMethod]
//public void MustUseProgramDataAs2ndPrio()
//{
// var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
appConfig.AppDataFolder = location;
// appConfig.ProgramDataFolder = location;
// appConfig.AppDataFolder = $@"{location}\WRONG";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.Perform();
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.Perform();
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
// var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
}
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
//}
[TestMethod]
public void MustFallbackToDefaultsAsLastPrio()
{
var actualSettings = default(Settings);
var defaultSettings = new Settings();
//[TestMethod]
//public void MustUseAppDataAs3rdPrio()
//{
// var location = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
// appConfig.AppDataFolder = location;
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.Perform();
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
session.VerifySet(s => s.Settings = defaultSettings);
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.Perform();
Assert.AreSame(defaultSettings, actualSettings);
}
// var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
[TestMethod]
public void MustAbortIfWishedByUser()
{
appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
//}
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
c.AbortStartup = true;
}
};
//[TestMethod]
//public void MustFallbackToDefaultsAsLastPrio()
//{
// var actualSettings = default(Settings);
// var defaultSettings = new Settings();
var result = sut.Perform();
// repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
// session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
Assert.AreEqual(OperationResult.Aborted, result);
}
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.Perform();
[TestMethod]
public void MustNotAbortIfNotWishedByUser()
{
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
// session.VerifySet(s => s.Settings = defaultSettings);
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
c.AbortStartup = false;
}
};
// Assert.AreSame(defaultSettings, actualSettings);
//}
var result = sut.Perform();
//[TestMethod]
//public void MustAbortIfWishedByUser()
//{
// appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
Assert.AreEqual(OperationResult.Success, result);
}
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is ConfigurationCompletedEventArgs c)
// {
// c.AbortStartup = true;
// }
// };
[TestMethod]
public void MustNotAllowToAbortIfNotInConfigureClientMode()
{
settings.ConfigurationMode = ConfigurationMode.Exam;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// var result = sut.Perform();
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is ConfigurationCompletedEventArgs c)
{
Assert.Fail();
}
};
// Assert.AreEqual(OperationResult.Aborted, result);
//}
sut.Perform();
}
//[TestMethod]
//public void MustNotAbortIfNotWishedByUser()
//{
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
[TestMethod]
public void MustNotFailWithoutCommandLineArgs()
{
var actualSettings = default(Settings);
var defaultSettings = new Settings();
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
Assert.AreSame(defaultSettings, actualSettings);
sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, sessionContext);
sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
Assert.AreSame(defaultSettings, actualSettings);
}
[TestMethod]
public void MustNotFailWithInvalidUri()
{
var uri = @"an/invalid\uri.'*%yolo/()你好";
sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, sessionContext);
sut.Perform();
}
[TestMethod]
public void MustOnlyAllowToEnterAdminPasswordFiveTimes()
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = true;
}
};
sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Exactly(5));
}
[TestMethod]
public void MustOnlyAllowToEnterSettingsPasswordFiveTimes()
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = true;
}
};
sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Exactly(5));
}
[TestMethod]
public void MustSucceedIfAdminPasswordCorrect()
{
var password = "test";
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Password = password;
p.Success = true;
}
};
sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password), Times.Once);
}
[TestMethod]
public void MustSucceedIfSettingsPasswordCorrect()
{
var password = "test";
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Password = password;
p.Success = true;
}
};
sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password), Times.Once);
}
[TestMethod]
public void MustAbortAskingForAdminPasswordIfDecidedByUser()
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = false;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void MustAbortAskingForSettingsPasswordIfDecidedByUser()
{
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Success = false;
}
};
var result = sut.Perform();
Assert.AreEqual(OperationResult.Aborted, result);
}
[TestMethod]
public void MustAllowEnteringBothPasswords()
{
var adminPassword = "xyz";
var settingsPassword = "abc";
var url = @"http://www.safeexambrowser.org/whatever.seb";
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
sut.ActionRequired += args =>
{
if (args is PasswordRequiredEventArgs p)
{
p.Password = p.Purpose == PasswordRequestPurpose.Administrator ? adminPassword : settingsPassword;
p.Success = true;
}
};
sut.Perform();
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword), Times.Once);
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword), Times.Once);
}
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is ConfigurationCompletedEventArgs c)
// {
// c.AbortStartup = false;
// }
// };
[TestMethod]
public void MustReconfigureSuccessfullyWithCorrectUri()
{
var location = Path.GetDirectoryName(GetType().Assembly.Location);
var resource = new Uri(Path.Combine(location, nameof(Operations), "SettingsDummy.txt"));
// var result = sut.Perform();
sessionContext.ReconfigurationFilePath = resource.LocalPath;
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null)).Returns(LoadStatus.Success);
// Assert.AreEqual(OperationResult.Success, result);
//}
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
//[TestMethod]
//public void MustNotAllowToAbortIfNotInConfigureClientMode()
//{
// settings.ConfigurationMode = ConfigurationMode.Exam;
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
var result = sut.Repeat();
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is ConfigurationCompletedEventArgs c)
// {
// Assert.Fail();
// }
// };
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
// sut.Perform();
//}
Assert.AreEqual(OperationResult.Success, result);
}
//[TestMethod]
//public void MustNotFailWithoutCommandLineArgs()
//{
// var actualSettings = default(Settings);
// var defaultSettings = new Settings();
// repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
// session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut.Perform();
// repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
// Assert.AreSame(defaultSettings, actualSettings);
// sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, sessionContext);
// sut.Perform();
// repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
// Assert.AreSame(defaultSettings, actualSettings);
//}
//[TestMethod]
//public void MustNotFailWithInvalidUri()
//{
// var uri = @"an/invalid\uri.'*%yolo/()你好";
// sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, sessionContext);
// sut.Perform();
//}
//[TestMethod]
//public void MustOnlyAllowToEnterAdminPasswordFiveTimes()
//{
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Success = true;
// }
// };
// sut.Perform();
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Exactly(5));
//}
//[TestMethod]
//public void MustOnlyAllowToEnterSettingsPasswordFiveTimes()
//{
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Success = true;
// }
// };
// sut.Perform();
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Exactly(5));
//}
//[TestMethod]
//public void MustSucceedIfAdminPasswordCorrect()
//{
// var password = "test";
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password)).Returns(LoadStatus.Success);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Password = password;
// p.Success = true;
// }
// };
// sut.Perform();
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password), Times.Once);
//}
//[TestMethod]
//public void MustSucceedIfSettingsPasswordCorrect()
//{
// var password = "test";
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password)).Returns(LoadStatus.Success);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Password = password;
// p.Success = true;
// }
// };
// sut.Perform();
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password), Times.Once);
//}
//[TestMethod]
//public void MustAbortAskingForAdminPasswordIfDecidedByUser()
//{
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Success = false;
// }
// };
// var result = sut.Perform();
// Assert.AreEqual(OperationResult.Aborted, result);
//}
//[TestMethod]
//public void MustAbortAskingForSettingsPasswordIfDecidedByUser()
//{
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Success = false;
// }
// };
// var result = sut.Perform();
// Assert.AreEqual(OperationResult.Aborted, result);
//}
//[TestMethod]
//public void MustAllowEnteringBothPasswords()
//{
// var adminPassword = "xyz";
// var settingsPassword = "abc";
// var url = @"http://www.safeexambrowser.org/whatever.seb";
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword)).Returns(LoadStatus.Success);
// sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
// sut.ActionRequired += args =>
// {
// if (args is PasswordRequiredEventArgs p)
// {
// p.Password = p.Purpose == PasswordRequestPurpose.Administrator ? adminPassword : settingsPassword;
// p.Success = true;
// }
// };
// sut.Perform();
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null), Times.Once);
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword), Times.Once);
// repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, settingsPassword), Times.Once);
//}
[TestMethod]
public void MustFailToReconfigureWithInvalidUri()
{
var resource = new Uri("file:///C:/does/not/exist.txt");
//[TestMethod]
//public void MustReconfigureSuccessfullyWithCorrectUri()
//{
// var location = Path.GetDirectoryName(GetType().Assembly.Location);
// var resource = new Uri(Path.Combine(location, nameof(Operations), "SettingsDummy.txt"));
sessionContext.ReconfigurationFilePath = null;
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// sessionContext.ReconfigurationFilePath = resource.LocalPath;
// repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null)).Returns(LoadStatus.Success);
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
var result = sut.Repeat();
// var result = sut.Repeat();
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Once);
sessionContext.ReconfigurationFilePath = resource.LocalPath;
result = sut.Repeat();
// Assert.AreEqual(OperationResult.Success, result);
//}
repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Never);
Assert.AreEqual(OperationResult.Failed, result);
}
//[TestMethod]
//public void MustFailToReconfigureWithInvalidUri()
//{
// var resource = new Uri("file:///C:/does/not/exist.txt");
// sessionContext.ReconfigurationFilePath = null;
// repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null)).Returns(LoadStatus.Success);
// sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
// var result = sut.Repeat();
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Never);
// Assert.AreEqual(OperationResult.Failed, result);
// sessionContext.ReconfigurationFilePath = resource.LocalPath;
// result = sut.Repeat();
// repository.Verify(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null), Times.Never);
// Assert.AreEqual(OperationResult.Failed, result);
//}
}
}

View file

@ -119,7 +119,7 @@ namespace SafeExamBrowser.Runtime
configuration = new ConfigurationRepository(repositoryLogger, executable.Location, programCopyright, programTitle, programVersion);
appConfig = configuration.InitializeAppConfig();
configuration.Register(new BinaryFormat(compressor, new ModuleLogger(logger, nameof(BinaryFormat))));
configuration.Register(new BinaryFormat(compressor, new HashAlgorithm(), new ModuleLogger(logger, nameof(BinaryFormat))));
configuration.Register(new XmlFormat(new ModuleLogger(logger, nameof(XmlFormat))));
configuration.Register(new FileResourceLoader(new ModuleLogger(logger, nameof(FileResourceLoader))));
configuration.Register(new NetworkResourceLoader(appConfig, new ModuleLogger(logger, nameof(NetworkResourceLoader))));

View file

@ -73,6 +73,7 @@ namespace SafeExamBrowser.Runtime.Operations
if (isValidUri)
{
result = LoadSettings(uri);
HandleClientConfiguration(ref result, uri);
}
else
{
@ -99,11 +100,16 @@ namespace SafeExamBrowser.Runtime.Operations
private OperationResult LoadSettings(Uri uri)
{
var status = configuration.TryLoadSettings(uri, out Settings settings);
var status = configuration.TryLoadSettings(uri, out Settings settings, Context.Current?.Settings?.AdminPasswordHash, true);
if (status == LoadStatus.PasswordNeeded)
{
status = configuration.TryLoadSettings(uri, out settings, string.Empty);
}
for (var attempts = 0; attempts < 5 && status == LoadStatus.PasswordNeeded; attempts++)
{
var result = TryGetPassword(status);
var result = TryGetPassword();
if (!result.Success)
{
@ -141,7 +147,7 @@ namespace SafeExamBrowser.Runtime.Operations
}
}
private PasswordRequiredEventArgs TryGetPassword(LoadStatus status)
private PasswordRequiredEventArgs TryGetPassword()
{
var purpose = PasswordRequestPurpose.Settings;
var args = new PasswordRequiredEventArgs { Purpose = purpose };
@ -203,7 +209,17 @@ namespace SafeExamBrowser.Runtime.Operations
if (successful && configureMode && !loadWithBrowser)
{
var args = new ConfigurationCompletedEventArgs();
var filePath = Path.Combine(Context.Next.AppConfig.AppDataFolder, Context.Next.AppConfig.DefaultSettingsFileName);
// TODO: Save / overwrite configuration file in APPDATA directory!
// -> Check whether current and new admin passwords are the same! If not, current needs to be verified before overwriting!
// -> Default settings password appears to be string.Empty for local client configuration
// -> Any (new?) certificates need to be imported and REMOVED from the settings before the data is saved!
//configuration.SaveSettings(Context.Next.Settings, filePath);
// TODO: If the client configuration happens while the application is already running, the new configuration should first
// be loaded and then the user should have the option to terminate!
// -> Introduce flag in Context, e.g. AskForTermination
ActionRequired?.Invoke(args);
logger.Info($"The user chose to {(args.AbortStartup ? "abort" : "continue")} after successful client configuration.");

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Core.OperationModel;
@ -51,6 +52,8 @@ namespace SafeExamBrowser.Runtime.Operations
public override OperationResult Revert()
{
FinalizeSessionConfiguration();
return OperationResult.Success;
}
@ -66,5 +69,11 @@ namespace SafeExamBrowser.Runtime.Operations
logger.Info($" -> Runtime-ID: {Context.Next.AppConfig.RuntimeId}");
logger.Info($" -> Session-ID: {Context.Next.Id}");
}
private void FinalizeSessionConfiguration()
{
Context.Current = null;
Context.Next = null;
}
}
}