SEBWIN-221: Improved configuration loading / parsing algorithm and implemented scaffolding for data mapping.
This commit is contained in:
parent
483ba7ad4a
commit
9b639b0c53
8 changed files with 240 additions and 134 deletions
|
@ -126,10 +126,10 @@ namespace SafeExamBrowser.Configuration
|
|||
|
||||
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string password = null, bool passwordIsHash = false)
|
||||
{
|
||||
settings = default(Settings);
|
||||
|
||||
logger.Info($"Attempting to load '{resource}'...");
|
||||
|
||||
settings = LoadDefaultSettings();
|
||||
|
||||
try
|
||||
{
|
||||
var status = TryLoadData(resource, out Stream data);
|
||||
|
@ -139,13 +139,13 @@ namespace SafeExamBrowser.Configuration
|
|||
switch (status)
|
||||
{
|
||||
case LoadStatus.LoadWithBrowser:
|
||||
return HandleBrowserResource(resource, out settings);
|
||||
return HandleBrowserResource(resource, settings);
|
||||
case LoadStatus.Success:
|
||||
return TryParseData(data, out settings, password, passwordIsHash);
|
||||
return TryParseData(data, settings, password, passwordIsHash);
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
return status;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
@ -175,16 +175,14 @@ namespace SafeExamBrowser.Configuration
|
|||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus TryParseData(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
|
||||
private LoadStatus TryParseData(Stream data, Settings settings, string password = null, bool passwordIsHash = false)
|
||||
{
|
||||
var status = LoadStatus.NotSupported;
|
||||
var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data));
|
||||
|
||||
settings = default(Settings);
|
||||
|
||||
if (dataFormat != null)
|
||||
{
|
||||
status = dataFormat.TryParse(data, out settings, password, passwordIsHash);
|
||||
status = dataFormat.TryParse(data, settings, password, passwordIsHash);
|
||||
logger.Info($"Tried to parse data from '{data}' using {dataFormat.GetType().Name} -> Result: {status}.");
|
||||
}
|
||||
else
|
||||
|
@ -195,11 +193,9 @@ namespace SafeExamBrowser.Configuration
|
|||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus HandleBrowserResource(Uri resource, out Settings settings)
|
||||
private LoadStatus HandleBrowserResource(Uri resource, Settings settings)
|
||||
{
|
||||
settings = LoadDefaultSettings();
|
||||
settings.Browser.StartUrl = resource.AbsoluteUri;
|
||||
|
||||
logger.Info($"The resource needs authentication or is HTML data, loaded default settings with '{resource}' as startup URL.");
|
||||
|
||||
return LoadStatus.Success;
|
||||
|
|
|
@ -57,13 +57,11 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return false;
|
||||
}
|
||||
|
||||
public LoadStatus TryParse(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
|
||||
public LoadStatus TryParse(Stream data, Settings settings, string password = null, bool passwordIsHash = false)
|
||||
{
|
||||
var prefix = ParsePrefix(data);
|
||||
var success = TryDetermineFormat(prefix, out FormatType format);
|
||||
|
||||
settings = default(Settings);
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (compressor.IsCompressed(data))
|
||||
|
@ -71,20 +69,20 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
data = compressor.Decompress(data);
|
||||
}
|
||||
|
||||
data = new SubStream(data, PREFIX_LENGTH, data.Length - PREFIX_LENGTH);
|
||||
logger.Debug($"Attempting to parse '{data}' with format '{prefix}'...");
|
||||
data = new SubStream(data, PREFIX_LENGTH, data.Length - PREFIX_LENGTH);
|
||||
|
||||
switch (format)
|
||||
{
|
||||
case FormatType.Password:
|
||||
case FormatType.PasswordConfigureClient:
|
||||
return ParsePasswordBlock(data, format, out settings, password, passwordIsHash);
|
||||
return ParsePasswordBlock(data, format, settings, password, passwordIsHash);
|
||||
case FormatType.PlainData:
|
||||
return ParsePlainDataBlock(data, out settings);
|
||||
return ParsePlainDataBlock(data, settings);
|
||||
case FormatType.PublicKeyHash:
|
||||
return ParsePublicKeyHashBlock(data, out settings, password, passwordIsHash);
|
||||
return ParsePublicKeyHashBlock(data, settings, password, passwordIsHash);
|
||||
case FormatType.PublicKeyHashWithSymmetricKey:
|
||||
return ParsePublicKeyHashWithSymmetricKeyBlock(data, out settings, password, passwordIsHash);
|
||||
return ParsePublicKeyHashWithSymmetricKeyBlock(data, settings, password, passwordIsHash);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -93,28 +91,36 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return LoadStatus.InvalidData;
|
||||
}
|
||||
|
||||
private LoadStatus ParsePasswordBlock(Stream data, FormatType format, out Settings settings, string password, bool passwordIsHash)
|
||||
private LoadStatus ParsePasswordBlock(Stream data, FormatType format, Settings settings, string password, bool passwordIsHash)
|
||||
{
|
||||
|
||||
settings = default(Settings);
|
||||
|
||||
if (format == FormatType.PasswordConfigureClient && !passwordIsHash)
|
||||
{
|
||||
password = hashAlgorithm.GenerateHashFor(password);
|
||||
}
|
||||
|
||||
var decrypted = default(Stream);
|
||||
var encryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption)));
|
||||
var status = encryption.Decrypt(data, out Stream decrypted, password);
|
||||
var status = default(LoadStatus);
|
||||
|
||||
if (format == FormatType.PasswordConfigureClient)
|
||||
{
|
||||
password = password == null ? string.Empty : (passwordIsHash ? password : hashAlgorithm.GenerateHashFor(password));
|
||||
status = encryption.Decrypt(data, out decrypted, password);
|
||||
|
||||
if (status == LoadStatus.PasswordNeeded && passwordIsHash && password != string.Empty)
|
||||
{
|
||||
status = encryption.Decrypt(data, out decrypted, string.Empty);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status = encryption.Decrypt(data, out decrypted, password);
|
||||
}
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
return ParsePlainDataBlock(decrypted, out settings);
|
||||
return ParsePlainDataBlock(decrypted, settings);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus ParsePlainDataBlock(Stream data, out Settings settings)
|
||||
private LoadStatus ParsePlainDataBlock(Stream data, Settings settings)
|
||||
{
|
||||
var xmlFormat = new XmlFormat(logger.CloneFor(nameof(XmlFormat)));
|
||||
|
||||
|
@ -123,36 +129,32 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
data = compressor.Decompress(data);
|
||||
}
|
||||
|
||||
return xmlFormat.TryParse(data, out settings);
|
||||
return xmlFormat.TryParse(data, settings);
|
||||
}
|
||||
|
||||
private LoadStatus ParsePublicKeyHashBlock(Stream data, out Settings settings, string password, bool passwordIsHash)
|
||||
private LoadStatus ParsePublicKeyHashBlock(Stream data, Settings settings, string password, bool passwordIsHash)
|
||||
{
|
||||
var encryption = new PublicKeyHashEncryption(logger.CloneFor(nameof(PublicKeyHashEncryption)));
|
||||
var status = encryption.Decrypt(data, out Stream decrypted);
|
||||
|
||||
settings = default(Settings);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
return TryParse(decrypted, out settings, password, passwordIsHash);
|
||||
return TryParse(decrypted, settings, password, passwordIsHash);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus ParsePublicKeyHashWithSymmetricKeyBlock(Stream data, out Settings settings, string password, bool passwordIsHash)
|
||||
private LoadStatus ParsePublicKeyHashWithSymmetricKeyBlock(Stream data, Settings settings, string password, bool passwordIsHash)
|
||||
{
|
||||
var logger = this.logger.CloneFor(nameof(PublicKeyHashWithSymmetricKeyEncryption));
|
||||
var passwordEncryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption)));
|
||||
var encryption = new PublicKeyHashWithSymmetricKeyEncryption(logger, passwordEncryption);
|
||||
var status = encryption.Decrypt(data, out Stream decrypted);
|
||||
|
||||
settings = default(Settings);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
return TryParse(decrypted, out settings, password, passwordIsHash);
|
||||
return TryParse(decrypted, settings, password, passwordIsHash);
|
||||
}
|
||||
|
||||
return status;
|
||||
|
|
|
@ -41,12 +41,6 @@ namespace SafeExamBrowser.Configuration.DataFormats.Cryptography
|
|||
}
|
||||
|
||||
var (version, options) = ParseHeader(data);
|
||||
|
||||
if (version != VERSION || options != OPTIONS)
|
||||
{
|
||||
return FailForInvalidHeader(version, options);
|
||||
}
|
||||
|
||||
var (authenticationKey, encryptionKey) = GenerateKeys(data, password);
|
||||
var (originalHmac, computedHmac) = GenerateHmac(data, authenticationKey);
|
||||
|
||||
|
@ -68,16 +62,14 @@ namespace SafeExamBrowser.Configuration.DataFormats.Cryptography
|
|||
var version = data.ReadByte();
|
||||
var options = data.ReadByte();
|
||||
|
||||
if (version != VERSION || options != OPTIONS)
|
||||
{
|
||||
logger.Warn($"Invalid encryption header! Expected: [{VERSION},{OPTIONS},...] - Actual: [{version},{options},...]");
|
||||
}
|
||||
|
||||
return (version, options);
|
||||
}
|
||||
|
||||
private LoadStatus FailForInvalidHeader(int version, int options)
|
||||
{
|
||||
logger.Error($"Invalid encryption header! Expected: [{VERSION},{OPTIONS},...] - Actual: [{version},{options},...]");
|
||||
|
||||
return LoadStatus.InvalidData;
|
||||
}
|
||||
|
||||
private (byte[] authenticationKey, byte[] encryptionKey) GenerateKeys(Stream data, string password)
|
||||
{
|
||||
var authenticationSalt = new byte[SALT_SIZE];
|
||||
|
|
54
SafeExamBrowser.Configuration/DataFormats/DataMapper.cs
Normal file
54
SafeExamBrowser.Configuration/DataFormats/DataMapper.cs
Normal file
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* 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.Collections.Generic;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.DataFormats
|
||||
{
|
||||
internal static class DataMapper
|
||||
{
|
||||
internal static void MapTo(this Dictionary<string, object> rawData, Settings settings)
|
||||
{
|
||||
foreach (var kvp in rawData)
|
||||
{
|
||||
Map(kvp.Key, kvp.Value, settings);
|
||||
}
|
||||
}
|
||||
|
||||
private static void Map(string key, object value, Settings settings)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "sebConfigPurpose":
|
||||
settings.MapConfigurationMode(value);
|
||||
break;
|
||||
case "startURL":
|
||||
settings.MapStartUrl(value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static void MapConfigurationMode(this Settings settings, object value)
|
||||
{
|
||||
if (value is Int32 mode)
|
||||
{
|
||||
settings.ConfigurationMode = mode == 1 ? ConfigurationMode.ConfigureClient : ConfigurationMode.Exam;
|
||||
}
|
||||
}
|
||||
|
||||
private static void MapStartUrl(this Settings settings, object value)
|
||||
{
|
||||
if (value is string startUrl)
|
||||
{
|
||||
settings.Browser.StartUrl = startUrl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Xml;
|
||||
|
@ -19,9 +20,6 @@ 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";
|
||||
|
||||
|
@ -61,151 +59,217 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return false;
|
||||
}
|
||||
|
||||
public LoadStatus TryParse(Stream data, out Settings settings, string password = null, bool passwordIsHash = false)
|
||||
public LoadStatus TryParse(Stream data, Settings settings, string password = null, bool passwordIsHash = false)
|
||||
{
|
||||
var status = LoadStatus.InvalidData;
|
||||
var xmlSettings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore };
|
||||
|
||||
settings = new Settings();
|
||||
data.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (var reader = XmlReader.Create(data, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
|
||||
using (var reader = XmlReader.Create(data, xmlSettings))
|
||||
{
|
||||
var hasRoot = reader.ReadToFollowing(ROOT_NODE);
|
||||
var hasDictionary = reader.ReadToDescendant(DataTypes.DICTIONARY);
|
||||
var rawData = new Dictionary<string, object>();
|
||||
|
||||
if (hasRoot)
|
||||
if (hasRoot && hasDictionary)
|
||||
{
|
||||
logger.Debug($"Found root node, starting to parse data...");
|
||||
Parse(reader, settings, out status);
|
||||
status = ParseDictionary(reader, rawData);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
logger.Debug("Mapping raw settings data...");
|
||||
rawData.MapTo(settings);
|
||||
}
|
||||
|
||||
logger.Debug($"Finished parsing -> Result: {status}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Could not find root node '{ROOT_NODE}'!");
|
||||
logger.Error($"Could not find root {(!hasRoot ? $"node '{ROOT_NODE}'" : $"dictionary '{DataTypes.DICTIONARY}'")}!");
|
||||
}
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private void Parse(XmlReader reader, Settings settings, out LoadStatus status)
|
||||
private LoadStatus ParseArray(XmlReader reader, List<object> array)
|
||||
{
|
||||
status = LoadStatus.Success;
|
||||
|
||||
if (reader.NodeType == XmlNodeType.Element)
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
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;
|
||||
}
|
||||
return LoadStatus.Success;
|
||||
}
|
||||
|
||||
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)
|
||||
var status = ParseElement(reader, out object element);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
ParseArray(reader, settings, out status);
|
||||
}
|
||||
else if (reader.Name == DICTIONARY)
|
||||
{
|
||||
ParseDictionary(reader, settings, out status);
|
||||
array.Add(element);
|
||||
}
|
||||
else
|
||||
{
|
||||
var item = XNode.ReadFrom(reader) as XElement;
|
||||
|
||||
// TODO: Map data...
|
||||
return status;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
reader.MoveToContent();
|
||||
}
|
||||
|
||||
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == ARRAY)
|
||||
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == DataTypes.ARRAY)
|
||||
{
|
||||
status = LoadStatus.Success;
|
||||
return LoadStatus.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Expected closing tag for '{ARRAY}', but found '{reader.Name}{reader.Value}'!");
|
||||
status = LoadStatus.InvalidData;
|
||||
logger.Error($"Expected closing tag for '{DataTypes.ARRAY}', but found '{reader.Name}{reader.Value}'!");
|
||||
|
||||
return LoadStatus.InvalidData;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseDictionary(XmlReader reader, Settings settings, out LoadStatus status)
|
||||
private LoadStatus ParseDictionary(XmlReader reader, Dictionary<string, object> dictionary)
|
||||
{
|
||||
if (reader.IsEmptyElement)
|
||||
{
|
||||
return LoadStatus.Success;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
reader.MoveToContent();
|
||||
|
||||
while (reader.NodeType == XmlNodeType.Element)
|
||||
{
|
||||
ParseKeyValuePair(reader, settings, out status);
|
||||
var status = ParseKeyValuePair(reader, dictionary);
|
||||
|
||||
if (status != LoadStatus.Success)
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
reader.MoveToContent();
|
||||
}
|
||||
|
||||
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == DICTIONARY)
|
||||
if (reader.NodeType == XmlNodeType.EndElement && reader.Name == DataTypes.DICTIONARY)
|
||||
{
|
||||
status = LoadStatus.Success;
|
||||
return LoadStatus.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Expected closing tag for '{DICTIONARY}', but found '{reader.Name}{reader.Value}'!");
|
||||
status = LoadStatus.InvalidData;
|
||||
logger.Error($"Expected closing tag for '{DataTypes.DICTIONARY}', but found '{reader.Name}{reader.Value}'!");
|
||||
|
||||
return LoadStatus.InvalidData;
|
||||
}
|
||||
}
|
||||
|
||||
private void ParseKeyValuePair(XmlReader reader, Settings settings, out LoadStatus status)
|
||||
private LoadStatus ParseKeyValuePair(XmlReader reader, Dictionary<string, object> dictionary)
|
||||
{
|
||||
var key = XNode.ReadFrom(reader) as XElement;
|
||||
|
||||
if (key.Name.LocalName != DataTypes.KEY)
|
||||
{
|
||||
logger.Error($"Expected element '{DataTypes.KEY}', but found '{key}'!");
|
||||
|
||||
return LoadStatus.InvalidData;
|
||||
}
|
||||
|
||||
reader.Read();
|
||||
reader.MoveToContent();
|
||||
|
||||
if (reader.Name == ARRAY)
|
||||
var status = ParseElement(reader, out object value);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
ParseArray(reader, settings, out status);
|
||||
dictionary[key.Value] = value;
|
||||
}
|
||||
else if (reader.Name == DICTIONARY)
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus ParseElement(XmlReader reader, out object element)
|
||||
{
|
||||
var array = default(List<object>);
|
||||
var dictionary = default(Dictionary<string, object>);
|
||||
var status = default(LoadStatus);
|
||||
var value = default(object);
|
||||
|
||||
if (reader.Name == DataTypes.ARRAY)
|
||||
{
|
||||
ParseDictionary(reader, settings, out status);
|
||||
array = new List<object>();
|
||||
status = ParseArray(reader, array);
|
||||
}
|
||||
else if (reader.Name == DataTypes.DICTIONARY)
|
||||
{
|
||||
dictionary = new Dictionary<string, object>();
|
||||
status = ParseDictionary(reader, dictionary);
|
||||
}
|
||||
else
|
||||
{
|
||||
var value = XNode.ReadFrom(reader) as XElement;
|
||||
|
||||
// TODO: Map data...
|
||||
|
||||
status = LoadStatus.Success;
|
||||
status = ParseSimpleType(XNode.ReadFrom(reader) as XElement, out value);
|
||||
}
|
||||
|
||||
element = array ?? dictionary ?? value;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus ParseSimpleType(XElement element, out object value)
|
||||
{
|
||||
value = null;
|
||||
|
||||
switch (element.Name.LocalName)
|
||||
{
|
||||
case DataTypes.DATA:
|
||||
value = Convert.FromBase64String(element.Value);
|
||||
break;
|
||||
case DataTypes.DATE:
|
||||
value = XmlConvert.ToDateTime(element.Value, XmlDateTimeSerializationMode.Utc);
|
||||
break;
|
||||
case DataTypes.FALSE:
|
||||
value = false;
|
||||
break;
|
||||
case DataTypes.INTEGER:
|
||||
value = Convert.ToInt32(element.Value);
|
||||
break;
|
||||
case DataTypes.REAL:
|
||||
value = Convert.ToDouble(element.Value);
|
||||
break;
|
||||
case DataTypes.STRING:
|
||||
value = element.Value;
|
||||
break;
|
||||
case DataTypes.TRUE:
|
||||
value = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (value == null)
|
||||
{
|
||||
logger.Error($"Element '{element}' is not supported!");
|
||||
|
||||
return LoadStatus.InvalidData;
|
||||
}
|
||||
|
||||
return LoadStatus.Success;
|
||||
}
|
||||
|
||||
private struct DataTypes
|
||||
{
|
||||
public const string ARRAY = "array";
|
||||
public const string DATA = "data";
|
||||
public const string DATE = "date";
|
||||
public const string DICTIONARY = "dict";
|
||||
public const string FALSE = "false";
|
||||
public const string INTEGER = "integer";
|
||||
public const string KEY = "key";
|
||||
public const string REAL = "real";
|
||||
public const string STRING = "string";
|
||||
public const string TRUE = "true";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,6 +64,7 @@
|
|||
<Compile Include="DataFormats\Cryptography\PublicKeyHashEncryption.cs" />
|
||||
<Compile Include="DataFormats\Cryptography\PublicKeyHashWithSymmetricKeyEncryption.cs" />
|
||||
<Compile Include="DataFormats\XmlFormat.cs" />
|
||||
<Compile Include="DataFormats\DataMapper.cs" />
|
||||
<Compile Include="HashAlgorithm.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ConfigurationRepository.cs" />
|
||||
|
|
|
@ -22,8 +22,8 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
|
||||
/// <summary>
|
||||
/// 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!
|
||||
/// the referenced settings can be in an undefinable state!
|
||||
/// </summary>
|
||||
LoadStatus TryParse(Stream data, out Settings.Settings settings, string password = null, bool passwordIsHash = false);
|
||||
LoadStatus TryParse(Stream data, Settings.Settings settings, string password = null, bool passwordIsHash = false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,11 +102,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
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();
|
||||
|
@ -213,13 +208,15 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
// 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!
|
||||
// -> Special case: If current admin password is same as new settings password, verification is not necessary!
|
||||
// -> 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!
|
||||
// -> DO NOT transform settings, just simply copy the given configuration file to %APPDATA%\SebClientSettings.seb !!!
|
||||
//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
|
||||
// -> Introduce flag in Context, e.g. AskForTermination?
|
||||
ActionRequired?.Invoke(args);
|
||||
|
||||
logger.Info($"The user chose to {(args.AbortStartup ? "abort" : "continue")} after successful client configuration.");
|
||||
|
|
Loading…
Reference in a new issue