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:
dbuechel 2020-01-30 15:20:05 +01:00
parent 15f9acd917
commit de00dbc13c
5 changed files with 76 additions and 41 deletions

View file

@ -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]

View file

@ -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>

View file

@ -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)

View file

@ -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);
}
} }
} }

View file

@ -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))));