From de00dbc13c80b2a51d794f83e60361960f01763b Mon Sep 17 00:00:00 2001 From: dbuechel Date: Thu, 30 Jan 2020 15:20:05 +0100 Subject: [PATCH] SEBWIN-352: Implemented new configuration format, i.e. parsing of compressed XML data. Also removed BOM from XML unit test data file. --- .../DataFormats/XmlParserTests.cs | 7 +- .../DataFormats/XmlTestData.xml | 2 +- .../DataFormats/BinaryParser.cs | 25 +++--- .../DataFormats/XmlParser.cs | 79 +++++++++++++------ SafeExamBrowser.Runtime/CompositionRoot.cs | 4 +- 5 files changed, 76 insertions(+), 41 deletions(-) diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs index 72a0c8fc..8dfce4dd 100644 --- a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlParserTests.cs @@ -15,9 +15,10 @@ using System.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Moq; using SafeExamBrowser.Configuration.ConfigurationData; -using SafeExamBrowser.Configuration.DataFormats; using SafeExamBrowser.Configuration.Contracts; +using SafeExamBrowser.Configuration.Contracts.DataCompression; using SafeExamBrowser.Configuration.Contracts.DataFormats; +using SafeExamBrowser.Configuration.DataFormats; using SafeExamBrowser.Logging.Contracts; namespace SafeExamBrowser.Configuration.UnitTests.DataFormats @@ -25,15 +26,17 @@ namespace SafeExamBrowser.Configuration.UnitTests.DataFormats [TestClass] public class XmlParserTests { + private Mock compressor; private Mock logger; private XmlParser sut; [TestInitialize] public void Initialize() { + compressor = new Mock(); logger = new Mock(); - sut = new XmlParser(logger.Object); + sut = new XmlParser(compressor.Object, logger.Object); } [TestMethod] diff --git a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlTestData.xml b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlTestData.xml index 10dcebae..bcd11987 100644 --- a/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlTestData.xml +++ b/SafeExamBrowser.Configuration.UnitTests/DataFormats/XmlTestData.xml @@ -1,4 +1,4 @@ - + someString diff --git a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs index 884bfce8..0cdeb406 100644 --- a/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs +++ b/SafeExamBrowser.Configuration/DataFormats/BinaryParser.cs @@ -79,6 +79,7 @@ namespace SafeExamBrowser.Configuration.DataFormats { var prefix = ReadPrefix(data); var isValid = IsValid(prefix); + var result = new ParseResult { Status = LoadStatus.InvalidData }; if (isValid) { @@ -89,18 +90,25 @@ namespace SafeExamBrowser.Configuration.DataFormats { case BinaryBlock.Password: case BinaryBlock.PasswordConfigureClient: - return ParsePasswordBlock(data, prefix, password); + result = ParsePasswordBlock(data, prefix, password); + break; case BinaryBlock.PlainData: - return ParsePlainDataBlock(data); + result = ParsePlainDataBlock(data); + break; case BinaryBlock.PublicKey: 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 new ParseResult { Status = LoadStatus.InvalidData }; + return result; } 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; logger.Debug("Attempting to parse plain data block..."); - var result = xmlParser.TryParse(data); - result.Format = FormatType.Binary; - - return result; + return xmlParser.TryParse(data); } private ParseResult ParsePublicKeyBlock(Stream data, string prefix, PasswordParameters password = null) diff --git a/SafeExamBrowser.Configuration/DataFormats/XmlParser.cs b/SafeExamBrowser.Configuration/DataFormats/XmlParser.cs index 92fd4c30..d1a2a3b6 100644 --- a/SafeExamBrowser.Configuration/DataFormats/XmlParser.cs +++ b/SafeExamBrowser.Configuration/DataFormats/XmlParser.cs @@ -14,6 +14,7 @@ using System.Xml; using System.Xml.Linq; using SafeExamBrowser.Configuration.Contracts; using SafeExamBrowser.Configuration.Contracts.Cryptography; +using SafeExamBrowser.Configuration.Contracts.DataCompression; using SafeExamBrowser.Configuration.Contracts.DataFormats; using SafeExamBrowser.Logging.Contracts; @@ -23,10 +24,12 @@ namespace SafeExamBrowser.Configuration.DataFormats { private const string XML_PREFIX = " prefixData.Length; + var longEnough = data.Length > XML_PREFIX.Length; if (longEnough) { - data.Seek(0, SeekOrigin.Begin); - data.Read(prefixData, 0, prefixData.Length); + var prefix = ReadPrefix(data); + var isValid = XML_PREFIX.Equals(prefix, StringComparison.OrdinalIgnoreCase); - var prefix = Encoding.UTF8.GetString(prefixData); - var isXml = prefix == XML_PREFIX; + logger.Debug($"'{data}' starting with '{prefix}' {(isValid ? "matches" : "does not match")} the {FormatType.Xml} format."); - logger.Debug($"'{data}' starting with '{prefix}' {(isXml ? "matches" : "does not match")} the {FormatType.Xml} format."); - - return isXml; + return isValid; } 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) { - var result = new ParseResult { Format = FormatType.Xml, Status = LoadStatus.InvalidData }; - var settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Ignore }; + var prefix = ReadPrefix(data); + var isValid = XML_PREFIX.Equals(prefix, StringComparison.OrdinalIgnoreCase); + var result = new ParseResult { Status = LoadStatus.InvalidData }; - data.Seek(0, SeekOrigin.Begin); - - using (var reader = XmlReader.Create(data, settings)) + if (isValid) { - var hasRoot = reader.ReadToFollowing(XmlElement.Root); - var hasDictionary = reader.ReadToDescendant(XmlElement.Dictionary); + data = compressor.IsCompressed(data) ? compressor.Decompress(data) : data; + 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); - result.RawData = rawData; + if (hasRoot && hasDictionary) + { + logger.Debug($"Found root node, starting to parse data..."); - logger.Debug($"Finished parsing -> Result: {result.Status}."); - } - else - { - logger.Error($"Could not find root {(!hasRoot ? $"node '{XmlElement.Root}'" : $"dictionary '{XmlElement.Dictionary}'")}!"); + result.Status = ParseDictionary(reader, out var rawData); + result.RawData = rawData; + + logger.Debug($"Finished parsing -> Result: {result.Status}."); + } + 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; @@ -269,5 +279,22 @@ namespace SafeExamBrowser.Configuration.DataFormats 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); + } } } diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index bbcfe4e9..aa432ca4 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -141,7 +141,7 @@ namespace SafeExamBrowser.Runtime var publicKeyEncryption = new PublicKeyEncryption(certificateStore, ModuleLogger(nameof(PublicKeyEncryption))); var symmetricEncryption = new PublicKeySymmetricEncryption(certificateStore, ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption); 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))); configuration = new ConfigurationRepository( @@ -169,7 +169,7 @@ namespace SafeExamBrowser.Runtime publicKeyEncryption, symmetricEncryption, 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 FileResourceLoader(ModuleLogger(nameof(FileResourceLoader)))); configuration.Register(new FileResourceSaver(ModuleLogger(nameof(FileResourceSaver))));