diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs
index c566a060..f746e212 100644
--- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs
+++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs
@@ -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
diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PlainData.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PlainData.cs
deleted file mode 100644
index f811a4bd..00000000
--- a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PlainData.cs
+++ /dev/null
@@ -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;
- }
- }
-}
diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.cs
index 0e96d1a6..ee01e132 100644
--- a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.cs
+++ b/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.cs
@@ -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
}
}
}
diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.Password.cs b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PasswordEncryption.cs
similarity index 69%
rename from SafeExamBrowser.Configuration/DataFormats/BinaryFormat.Password.cs
rename to SafeExamBrowser.Configuration/DataFormats/Cryptography/PasswordEncryption.cs
index 000e5871..b8dedd08 100644
--- a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.Password.cs
+++ b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PasswordEncryption.cs
@@ -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];
diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHash.cs b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashEncryption.cs
similarity index 57%
rename from SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHash.cs
rename to SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashEncryption.cs
index ed848ffa..7ea90761 100644
--- a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHash.cs
+++ b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashEncryption.cs
@@ -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();
}
diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHashWithSymmetricKey.cs b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs
similarity index 56%
rename from SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHashWithSymmetricKey.cs
rename to SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs
index 439f9d8f..d7be15ae 100644
--- a/SafeExamBrowser.Configuration/DataFormats/BinaryFormat.PublicKeyHashWithSymmetricKey.cs
+++ b/SafeExamBrowser.Configuration/DataFormats/Cryptography/PublicKeyHashWithSymmetricKeyEncryption.cs
@@ -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();
}
diff --git a/SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs b/SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
index a3fa0ae3..04ef2552 100644
--- a/SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
+++ b/SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
@@ -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 = " 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;
+ }
}
}
}
diff --git a/SafeExamBrowser.Configuration/HashAlgorithm.cs b/SafeExamBrowser.Configuration/HashAlgorithm.cs
new file mode 100644
index 00000000..c1404394
--- /dev/null
+++ b/SafeExamBrowser.Configuration/HashAlgorithm.cs
@@ -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;
+ }
+ }
+ }
+}
diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
index c3e497ad..1ee5ecdd 100644
--- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
+++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj
@@ -54,23 +54,17 @@
+
+
-
- BinaryFormat.cs
-
-
- BinaryFormat.cs
-
-
- BinaryFormat.cs
-
-
- BinaryFormat.cs
-
+
+
+
+
diff --git a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
index fd68ebda..f686709c 100644
--- a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
+++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
@@ -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
/// , the referenced settings may be null or in an undefinable state!
///
- 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);
}
}
diff --git a/SafeExamBrowser.Contracts/Configuration/IDataFormat.cs b/SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
index dd2c6c37..621541cc 100644
--- a/SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
+++ b/SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
@@ -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 ,
/// the referenced settings may be null or in an undefinable state!
///
- 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);
}
}
diff --git a/SafeExamBrowser.Contracts/Configuration/IHashAlgorithm.cs b/SafeExamBrowser.Contracts/Configuration/IHashAlgorithm.cs
new file mode 100644
index 00000000..45d3148f
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Configuration/IHashAlgorithm.cs
@@ -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
+{
+ ///
+ /// Provides functionality to calculate hash codes of different objects.
+ ///
+ public interface IHashAlgorithm
+ {
+ ///
+ /// Computes a hash code for the given password.
+ ///
+ string GenerateHashFor(string password);
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Configuration/Settings/Settings.cs b/SafeExamBrowser.Contracts/Configuration/Settings/Settings.cs
index 3d7ab19b..ba11b4e3 100644
--- a/SafeExamBrowser.Contracts/Configuration/Settings/Settings.cs
+++ b/SafeExamBrowser.Contracts/Configuration/Settings/Settings.cs
@@ -16,6 +16,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
[Serializable]
public class Settings
{
+ ///
+ /// The hash code of the administrator password for the settings.
+ ///
+ public string AdminPasswordHash { get; set; }
+
///
/// The mode which determines the configuration behaviour.
///
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index ec08d424..53e764c7 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -55,6 +55,7 @@
+
diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs
index 786916fe..0e4bb788 100644
--- a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs
+++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs
@@ -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(), 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(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(), out settings, null)).Returns(LoadStatus.Success);
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(u => u.Equals(resource)), out settings, null), Times.Once);
- }
+ // repository.Verify(r => r.TryLoadSettings(It.Is(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(), out settings, null)).Returns(LoadStatus.Success);
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(u => u.Equals(resource)), out settings, null), Times.Once);
- }
+ // repository.Verify(r => r.TryLoadSettings(It.Is(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(s => s.Settings = It.IsAny()).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(), 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(), out settings, null)).Returns(LoadStatus.Success);
+ // repository.Verify(r => r.TryLoadSettings(It.Is(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(s => s.Settings = It.IsAny()).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(), 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(), 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(), 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(), 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(s => s.Settings = It.IsAny()).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(), 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(), 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(), 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(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
- repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
- repository.Verify(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
- repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
- repository.Verify(r => r.TryLoadSettings(It.IsAny(), out settings, password), Times.Once);
- }
-
- [TestMethod]
- public void MustAbortAskingForAdminPasswordIfDecidedByUser()
- {
- var url = @"http://www.safeexambrowser.org/whatever.seb";
-
- repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
- repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
- repository.Verify(r => r.TryLoadSettings(It.IsAny(), out settings, settingsPassword), Times.Once);
- repository.Verify(r => r.TryLoadSettings(It.IsAny(), 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(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(), 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(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(s => s.Settings = It.IsAny()).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(), 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(), 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(), 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(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
+ // repository.Verify(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
+ // repository.Verify(r => r.TryLoadSettings(It.IsAny(), out settings, password), Times.Once);
+ //}
+
+ //[TestMethod]
+ //public void MustAbortAskingForAdminPasswordIfDecidedByUser()
+ //{
+ // var url = @"http://www.safeexambrowser.org/whatever.seb";
+
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), 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(), out settings, null)).Returns(LoadStatus.PasswordNeeded);
+ // repository.Setup(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null), Times.Once);
+ // repository.Verify(r => r.TryLoadSettings(It.IsAny(), out settings, settingsPassword), Times.Once);
+ // repository.Verify(r => r.TryLoadSettings(It.IsAny(), 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(), out settings, null)).Returns(LoadStatus.Success);
+ // sessionContext.ReconfigurationFilePath = resource.LocalPath;
+ // repository.Setup(r => r.TryLoadSettings(It.Is(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(u => u.Equals(resource)), out settings, null), Times.Never);
- Assert.AreEqual(OperationResult.Failed, result);
+ // repository.Verify(r => r.TryLoadSettings(It.Is(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(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(), 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(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(u => u.Equals(resource)), out settings, null), Times.Never);
+ // Assert.AreEqual(OperationResult.Failed, result);
+ //}
}
}
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index 195d0ede..29cb066c 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -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))));
diff --git a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
index 34f56dc7..12ed2973 100644
--- a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs
@@ -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.");
diff --git a/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs b/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
index a60ed095..56c3a9e9 100644
--- a/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
+++ b/SafeExamBrowser.Runtime/Operations/SessionInitializationOperation.cs
@@ -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;
+ }
}
}