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 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<IDataCompressor> compressor;
|
||||
private Mock<ILogger> logger;
|
||||
private XmlParser sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
compressor = new Mock<IDataCompressor>();
|
||||
logger = new Mock<ILogger>();
|
||||
|
||||
sut = new XmlParser(logger.Object);
|
||||
sut = new XmlParser(compressor.Object, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<plist>
|
||||
<dict>
|
||||
<key>someString</key>
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = "<?xm";
|
||||
|
||||
private IDataCompressor compressor;
|
||||
private ILogger logger;
|
||||
|
||||
public XmlParser(ILogger logger)
|
||||
public XmlParser(IDataCompressor compressor, ILogger logger)
|
||||
{
|
||||
this.compressor = compressor;
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
|
@ -34,20 +37,16 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
{
|
||||
try
|
||||
{
|
||||
var prefixData = new byte[XML_PREFIX.Length];
|
||||
var longEnough = data.Length > 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))));
|
||||
|
|
Loading…
Add table
Reference in a new issue