SEBWIN-352: Implemented new configuration format, i.e. parsing of compressed XML data. Also removed BOM from XML unit test data file.
This commit is contained in:
parent
15f9acd917
commit
de00dbc13c
5 changed files with 76 additions and 41 deletions
|
@ -15,9 +15,10 @@ using System.Text;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
using SafeExamBrowser.Configuration.ConfigurationData;
|
using SafeExamBrowser.Configuration.ConfigurationData;
|
||||||
using SafeExamBrowser.Configuration.DataFormats;
|
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
|
using SafeExamBrowser.Configuration.Contracts.DataCompression;
|
||||||
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
||||||
|
using SafeExamBrowser.Configuration.DataFormats;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
|
namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
|
||||||
|
@ -25,15 +26,17 @@ namespace SafeExamBrowser.Configuration.UnitTests.DataFormats
|
||||||
[TestClass]
|
[TestClass]
|
||||||
public class XmlParserTests
|
public class XmlParserTests
|
||||||
{
|
{
|
||||||
|
private Mock<IDataCompressor> compressor;
|
||||||
private Mock<ILogger> logger;
|
private Mock<ILogger> logger;
|
||||||
private XmlParser sut;
|
private XmlParser sut;
|
||||||
|
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
|
compressor = new Mock<IDataCompressor>();
|
||||||
logger = new Mock<ILogger>();
|
logger = new Mock<ILogger>();
|
||||||
|
|
||||||
sut = new XmlParser(logger.Object);
|
sut = new XmlParser(compressor.Object, logger.Object);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<plist>
|
<plist>
|
||||||
<dict>
|
<dict>
|
||||||
<key>someString</key>
|
<key>someString</key>
|
||||||
|
|
|
@ -79,6 +79,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
{
|
{
|
||||||
var prefix = ReadPrefix(data);
|
var prefix = ReadPrefix(data);
|
||||||
var isValid = IsValid(prefix);
|
var isValid = IsValid(prefix);
|
||||||
|
var result = new ParseResult { Status = LoadStatus.InvalidData };
|
||||||
|
|
||||||
if (isValid)
|
if (isValid)
|
||||||
{
|
{
|
||||||
|
@ -89,18 +90,25 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
{
|
{
|
||||||
case BinaryBlock.Password:
|
case BinaryBlock.Password:
|
||||||
case BinaryBlock.PasswordConfigureClient:
|
case BinaryBlock.PasswordConfigureClient:
|
||||||
return ParsePasswordBlock(data, prefix, password);
|
result = ParsePasswordBlock(data, prefix, password);
|
||||||
|
break;
|
||||||
case BinaryBlock.PlainData:
|
case BinaryBlock.PlainData:
|
||||||
return ParsePlainDataBlock(data);
|
result = ParsePlainDataBlock(data);
|
||||||
|
break;
|
||||||
case BinaryBlock.PublicKey:
|
case BinaryBlock.PublicKey:
|
||||||
case BinaryBlock.PublicKeySymmetric:
|
case BinaryBlock.PublicKeySymmetric:
|
||||||
return ParsePublicKeyBlock(data, prefix, password);
|
result = ParsePublicKeyBlock(data, prefix, password);
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.Format = FormatType.Binary;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error($"'{data}' starting with '{prefix}' does not match the {FormatType.Binary} format!");
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Error($"'{data}' starting with '{prefix}' does not match the {FormatType.Binary} format!");
|
return result;
|
||||||
|
|
||||||
return new ParseResult { Status = LoadStatus.InvalidData };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParseResult ParsePasswordBlock(Stream data, string prefix, PasswordParameters password = null)
|
private ParseResult ParsePasswordBlock(Stream data, string prefix, PasswordParameters password = null)
|
||||||
|
@ -133,10 +141,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
data = compressor.IsCompressed(data) ? compressor.Decompress(data) : data;
|
data = compressor.IsCompressed(data) ? compressor.Decompress(data) : data;
|
||||||
logger.Debug("Attempting to parse plain data block...");
|
logger.Debug("Attempting to parse plain data block...");
|
||||||
|
|
||||||
var result = xmlParser.TryParse(data);
|
return xmlParser.TryParse(data);
|
||||||
result.Format = FormatType.Binary;
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ParseResult ParsePublicKeyBlock(Stream data, string prefix, PasswordParameters password = null)
|
private ParseResult ParsePublicKeyBlock(Stream data, string prefix, PasswordParameters password = null)
|
||||||
|
|
|
@ -14,6 +14,7 @@ using System.Xml;
|
||||||
using System.Xml.Linq;
|
using System.Xml.Linq;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||||
|
using SafeExamBrowser.Configuration.Contracts.DataCompression;
|
||||||
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
|
||||||
|
@ -23,10 +24,12 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
{
|
{
|
||||||
private const string XML_PREFIX = "<?xm";
|
private const string XML_PREFIX = "<?xm";
|
||||||
|
|
||||||
|
private IDataCompressor compressor;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
|
|
||||||
public XmlParser(ILogger logger)
|
public XmlParser(IDataCompressor compressor, ILogger logger)
|
||||||
{
|
{
|
||||||
|
this.compressor = compressor;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -34,20 +37,16 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var prefixData = new byte[XML_PREFIX.Length];
|
var longEnough = data.Length > XML_PREFIX.Length;
|
||||||
var longEnough = data.Length > prefixData.Length;
|
|
||||||
|
|
||||||
if (longEnough)
|
if (longEnough)
|
||||||
{
|
{
|
||||||
data.Seek(0, SeekOrigin.Begin);
|
var prefix = ReadPrefix(data);
|
||||||
data.Read(prefixData, 0, prefixData.Length);
|
var isValid = XML_PREFIX.Equals(prefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
|
||||||
var prefix = Encoding.UTF8.GetString(prefixData);
|
logger.Debug($"'{data}' starting with '{prefix}' {(isValid ? "matches" : "does not match")} the {FormatType.Xml} format.");
|
||||||
var isXml = prefix == XML_PREFIX;
|
|
||||||
|
|
||||||
logger.Debug($"'{data}' starting with '{prefix}' {(isXml ? "matches" : "does not match")} the {FormatType.Xml} format.");
|
return isValid;
|
||||||
|
|
||||||
return isXml;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug($"'{data}' is not long enough ({data.Length} bytes) to match the {FormatType.Xml} format.");
|
logger.Debug($"'{data}' is not long enough ({data.Length} bytes) to match the {FormatType.Xml} format.");
|
||||||
|
@ -62,29 +61,40 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
|
|
||||||
public ParseResult TryParse(Stream data, PasswordParameters password = null)
|
public ParseResult TryParse(Stream data, PasswordParameters password = null)
|
||||||
{
|
{
|
||||||
var result = new ParseResult { Format = FormatType.Xml, Status = LoadStatus.InvalidData };
|
var prefix = ReadPrefix(data);
|
||||||
var settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore };
|
var isValid = XML_PREFIX.Equals(prefix, StringComparison.OrdinalIgnoreCase);
|
||||||
|
var result = new ParseResult { Status = LoadStatus.InvalidData };
|
||||||
|
|
||||||
data.Seek(0, SeekOrigin.Begin);
|
if (isValid)
|
||||||
|
|
||||||
using (var reader = XmlReader.Create(data, settings))
|
|
||||||
{
|
{
|
||||||
var hasRoot = reader.ReadToFollowing(XmlElement.Root);
|
data = compressor.IsCompressed(data) ? compressor.Decompress(data) : data;
|
||||||
var hasDictionary = reader.ReadToDescendant(XmlElement.Dictionary);
|
data.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
if (hasRoot && hasDictionary)
|
using (var reader = XmlReader.Create(data, new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }))
|
||||||
{
|
{
|
||||||
logger.Debug($"Found root node, starting to parse data...");
|
var hasRoot = reader.ReadToFollowing(XmlElement.Root);
|
||||||
|
var hasDictionary = reader.ReadToDescendant(XmlElement.Dictionary);
|
||||||
|
|
||||||
result.Status = ParseDictionary(reader, out var rawData);
|
if (hasRoot && hasDictionary)
|
||||||
result.RawData = rawData;
|
{
|
||||||
|
logger.Debug($"Found root node, starting to parse data...");
|
||||||
|
|
||||||
logger.Debug($"Finished parsing -> Result: {result.Status}.");
|
result.Status = ParseDictionary(reader, out var rawData);
|
||||||
}
|
result.RawData = rawData;
|
||||||
else
|
|
||||||
{
|
logger.Debug($"Finished parsing -> Result: {result.Status}.");
|
||||||
logger.Error($"Could not find root {(!hasRoot ? $"node '{XmlElement.Root}'" : $"dictionary '{XmlElement.Dictionary}'")}!");
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error($"Could not find root {(!hasRoot ? $"node '{XmlElement.Root}'" : $"dictionary '{XmlElement.Dictionary}'")}!");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
result.Format = FormatType.Xml;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Error($"'{data}' starting with '{prefix}' does not match the {FormatType.Xml} format!");
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
@ -269,5 +279,22 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string ReadPrefix(Stream data)
|
||||||
|
{
|
||||||
|
var prefixData = new byte[XML_PREFIX.Length];
|
||||||
|
|
||||||
|
if (compressor.IsCompressed(data))
|
||||||
|
{
|
||||||
|
prefixData = compressor.Peek(data, XML_PREFIX.Length);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
data.Seek(0, SeekOrigin.Begin);
|
||||||
|
data.Read(prefixData, 0, XML_PREFIX.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Encoding.UTF8.GetString(prefixData);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -141,7 +141,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
var publicKeyEncryption = new PublicKeyEncryption(certificateStore, ModuleLogger(nameof(PublicKeyEncryption)));
|
var publicKeyEncryption = new PublicKeyEncryption(certificateStore, ModuleLogger(nameof(PublicKeyEncryption)));
|
||||||
var symmetricEncryption = new PublicKeySymmetricEncryption(certificateStore, ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption);
|
var symmetricEncryption = new PublicKeySymmetricEncryption(certificateStore, ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption);
|
||||||
var repositoryLogger = ModuleLogger(nameof(ConfigurationRepository));
|
var repositoryLogger = ModuleLogger(nameof(ConfigurationRepository));
|
||||||
var xmlParser = new XmlParser(ModuleLogger(nameof(XmlParser)));
|
var xmlParser = new XmlParser(compressor, ModuleLogger(nameof(XmlParser)));
|
||||||
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
|
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
|
||||||
|
|
||||||
configuration = new ConfigurationRepository(
|
configuration = new ConfigurationRepository(
|
||||||
|
@ -169,7 +169,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
publicKeyEncryption,
|
publicKeyEncryption,
|
||||||
symmetricEncryption,
|
symmetricEncryption,
|
||||||
xmlSerializer));
|
xmlSerializer));
|
||||||
configuration.Register(new XmlParser(ModuleLogger(nameof(XmlParser))));
|
configuration.Register(new XmlParser(compressor, ModuleLogger(nameof(XmlParser))));
|
||||||
configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer))));
|
configuration.Register(new XmlSerializer(ModuleLogger(nameof(XmlSerializer))));
|
||||||
configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader))));
|
configuration.Register(new FileResourceLoader(ModuleLogger(nameof(FileResourceLoader))));
|
||||||
configuration.Register(new FileResourceSaver(ModuleLogger(nameof(FileResourceSaver))));
|
configuration.Register(new FileResourceSaver(ModuleLogger(nameof(FileResourceSaver))));
|
||||||
|
|
Loading…
Add table
Reference in a new issue