SEBWIN-296: Implemented unit tests for configuration repository.
This commit is contained in:
parent
f8cb521a1e
commit
5016c14ff3
7 changed files with 333 additions and 90 deletions
|
@ -7,11 +7,16 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
using Moq;
|
using Moq;
|
||||||
|
using SafeExamBrowser.Configuration.ConfigurationData;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration.DataFormats;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration.DataResources;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.UnitTests
|
namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
|
@ -20,19 +25,252 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
public class ConfigurationRepositoryTests
|
public class ConfigurationRepositoryTests
|
||||||
{
|
{
|
||||||
private ConfigurationRepository sut;
|
private ConfigurationRepository sut;
|
||||||
|
private Mock<IDataParser> binaryParser;
|
||||||
|
private Mock<IDataSerializer> binarySerializer;
|
||||||
|
private Mock<ICertificateStore> certificateStore;
|
||||||
|
private Mock<IResourceLoader> fileLoader;
|
||||||
|
private Mock<IResourceSaver> fileSaver;
|
||||||
|
private Mock<IHashAlgorithm> hashAlgorithm;
|
||||||
|
private Mock<IModuleLogger> logger;
|
||||||
|
private Mock<IResourceLoader> networkLoader;
|
||||||
|
private Mock<IDataParser> xmlParser;
|
||||||
|
private Mock<IDataSerializer> xmlSerializer;
|
||||||
|
|
||||||
[TestInitialize]
|
[TestInitialize]
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
var executablePath = Assembly.GetExecutingAssembly().Location;
|
var executablePath = Assembly.GetExecutingAssembly().Location;
|
||||||
var hashAlgorithm = new Mock<IHashAlgorithm>();
|
|
||||||
var logger = new Mock<IModuleLogger>();
|
|
||||||
|
|
||||||
sut = new ConfigurationRepository(hashAlgorithm.Object, logger.Object, executablePath, string.Empty, string.Empty, string.Empty);
|
binaryParser = new Mock<IDataParser>();
|
||||||
|
binarySerializer = new Mock<IDataSerializer>();
|
||||||
|
certificateStore = new Mock<ICertificateStore>();
|
||||||
|
fileLoader = new Mock<IResourceLoader>();
|
||||||
|
fileSaver = new Mock<IResourceSaver>();
|
||||||
|
hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||||
|
logger = new Mock<IModuleLogger>();
|
||||||
|
networkLoader = new Mock<IResourceLoader>();
|
||||||
|
xmlParser = new Mock<IDataParser>();
|
||||||
|
xmlSerializer = new Mock<IDataSerializer>();
|
||||||
|
|
||||||
|
fileLoader.Setup(f => f.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
|
||||||
|
fileSaver.Setup(f => f.CanSave(It.IsAny<Uri>())).Returns<Uri>(u => u.IsFile);
|
||||||
|
networkLoader.Setup(n => n.CanLoad(It.IsAny<Uri>())).Returns<Uri>(u => u.Scheme.Equals("http") || u.Scheme.Equals("seb"));
|
||||||
|
|
||||||
|
sut = new ConfigurationRepository(certificateStore.Object, hashAlgorithm.Object, logger.Object, executablePath, string.Empty, string.Empty, string.Empty);
|
||||||
|
sut.InitializeAppConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustCorrectlyInitializeSessionConfiguration()
|
public void ConfigureClient_MustWorkAsExpected()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream() as Stream;
|
||||||
|
var password = new PasswordParameters { Password = "test123" };
|
||||||
|
var parseResult = new ParseResult
|
||||||
|
{
|
||||||
|
Format = FormatType.Binary,
|
||||||
|
RawData = new Dictionary<string, object>(),
|
||||||
|
Status = LoadStatus.Success
|
||||||
|
};
|
||||||
|
var serializeResult = new SerializeResult
|
||||||
|
{
|
||||||
|
Data = new MemoryStream(),
|
||||||
|
Status = SaveStatus.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
|
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||||
|
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||||
|
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
|
||||||
|
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<PasswordParameters>())).Returns(serializeResult);
|
||||||
|
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
|
||||||
|
|
||||||
|
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||||
|
|
||||||
|
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||||
|
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
|
||||||
|
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Once);
|
||||||
|
binarySerializer.Verify(b => b.TrySerialize(
|
||||||
|
It.IsAny<Dictionary<string, object>>(),
|
||||||
|
It.Is<PasswordParameters>(p => p.IsHash == true && p.Password == string.Empty)), Times.Once);
|
||||||
|
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreEqual(SaveStatus.Success, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConfigureClient_MustKeepSameEncryptionAccordingToConfiguration()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream() as Stream;
|
||||||
|
var password = new PasswordParameters { Password = "test123" };
|
||||||
|
var parseResult = new ParseResult
|
||||||
|
{
|
||||||
|
Encryption = new PublicKeyParameters
|
||||||
|
{
|
||||||
|
InnerEncryption = password,
|
||||||
|
SymmetricEncryption = true
|
||||||
|
},
|
||||||
|
Format = FormatType.Binary,
|
||||||
|
RawData = new Dictionary<string, object> { { Keys.ConfigurationFile.KeepClientConfigEncryption, true } },
|
||||||
|
Status = LoadStatus.Success
|
||||||
|
};
|
||||||
|
var serializeResult = new SerializeResult
|
||||||
|
{
|
||||||
|
Data = new MemoryStream(),
|
||||||
|
Status = SaveStatus.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
|
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||||
|
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||||
|
binarySerializer.Setup(b => b.CanSerialize(FormatType.Binary)).Returns(true);
|
||||||
|
binarySerializer.Setup(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>())).Returns(serializeResult);
|
||||||
|
fileSaver.Setup(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>())).Returns(SaveStatus.Success);
|
||||||
|
|
||||||
|
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||||
|
|
||||||
|
binarySerializer.Verify(b => b.TrySerialize(
|
||||||
|
It.IsAny<Dictionary<string, object>>(),
|
||||||
|
It.Is<PublicKeyParameters>(p => p.InnerEncryption == password && p.SymmetricEncryption)), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreEqual(SaveStatus.Success, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void ConfigureClient_MustAbortProcessOnError()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream() as Stream;
|
||||||
|
var password = new PasswordParameters { Password = "test123" };
|
||||||
|
var parseResult = new ParseResult
|
||||||
|
{
|
||||||
|
Format = FormatType.Binary,
|
||||||
|
RawData = new Dictionary<string, object>(),
|
||||||
|
Status = LoadStatus.Success
|
||||||
|
};
|
||||||
|
var serializeResult = new SerializeResult
|
||||||
|
{
|
||||||
|
Data = new MemoryStream(),
|
||||||
|
Status = SaveStatus.Success
|
||||||
|
};
|
||||||
|
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
|
fileLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
|
||||||
|
|
||||||
|
var status = sut.ConfigureClientWith(new Uri("C:\\TEMP\\Some\\file.seb"), password);
|
||||||
|
|
||||||
|
fileLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||||
|
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
|
||||||
|
certificateStore.Verify(c => c.ExtractAndImportIdentities(It.IsAny<Dictionary<string, object>>()), Times.Never);
|
||||||
|
binarySerializer.Verify(b => b.TrySerialize(It.IsAny<Dictionary<string, object>>(), It.IsAny<EncryptionParameters>()), Times.Never);
|
||||||
|
fileSaver.Verify(f => f.TrySave(It.IsAny<Uri>(), It.IsAny<Stream>()), Times.Never);
|
||||||
|
|
||||||
|
Assert.AreEqual(SaveStatus.UnexpectedError, status);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TryLoad_MustWorkAsExpected()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream() as Stream;
|
||||||
|
var parseResult = new ParseResult { RawData = new Dictionary<string, object>(), Status = LoadStatus.Success };
|
||||||
|
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
|
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||||
|
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||||
|
|
||||||
|
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||||
|
fileLoader.Verify(f => f.TryLoad(It.IsAny<Uri>(), out stream), Times.Never);
|
||||||
|
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||||
|
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out stream), Times.Once);
|
||||||
|
binaryParser.Verify(b => b.CanParse(It.IsAny<Stream>()), Times.Once);
|
||||||
|
binaryParser.Verify(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Once);
|
||||||
|
xmlParser.Verify(x => x.CanParse(It.IsAny<Stream>()), Times.AtMostOnce);
|
||||||
|
xmlParser.Verify(x => x.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>()), Times.Never);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.Success, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TryLoad_MustReportPasswordNeed()
|
||||||
|
{
|
||||||
|
var stream = new MemoryStream() as Stream;
|
||||||
|
var parseResult = new ParseResult { Status = LoadStatus.PasswordNeeded };
|
||||||
|
|
||||||
|
RegisterModules();
|
||||||
|
|
||||||
|
networkLoader.Setup(n => n.TryLoad(It.IsAny<Uri>(), out stream)).Returns(LoadStatus.Success);
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Returns(true);
|
||||||
|
binaryParser.Setup(b => b.TryParse(It.IsAny<Stream>(), It.IsAny<PasswordParameters>())).Returns(parseResult);
|
||||||
|
|
||||||
|
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.PasswordNeeded, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TryLoad_MustNotFailToIfNoLoaderRegistered()
|
||||||
|
{
|
||||||
|
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||||
|
|
||||||
|
sut.Register(fileLoader.Object);
|
||||||
|
sut.Register(networkLoader.Object);
|
||||||
|
|
||||||
|
result = sut.TryLoadSettings(new Uri("ftp://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
fileLoader.Verify(f => f.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||||
|
networkLoader.Verify(n => n.CanLoad(It.IsAny<Uri>()), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TryLoad_MustNotFailIfNoParserRegistered()
|
||||||
|
{
|
||||||
|
var data = default(Stream);
|
||||||
|
|
||||||
|
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
|
||||||
|
sut.Register(networkLoader.Object);
|
||||||
|
|
||||||
|
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
networkLoader.Verify(n => n.TryLoad(It.IsAny<Uri>(), out data), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.NotSupported, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void TryLoad_MustNotFailInCaseOfUnexpectedError()
|
||||||
|
{
|
||||||
|
var data = default(Stream);
|
||||||
|
|
||||||
|
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Throws<Exception>();
|
||||||
|
sut.Register(networkLoader.Object);
|
||||||
|
|
||||||
|
var result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.UnexpectedError, result);
|
||||||
|
|
||||||
|
binaryParser.Setup(b => b.CanParse(It.IsAny<Stream>())).Throws<Exception>();
|
||||||
|
networkLoader.Setup(l => l.TryLoad(It.IsAny<Uri>(), out data)).Returns(LoadStatus.Success);
|
||||||
|
sut.Register(binaryParser.Object);
|
||||||
|
|
||||||
|
result = sut.TryLoadSettings(new Uri("http://www.blubb.org"), out _);
|
||||||
|
|
||||||
|
Assert.AreEqual(LoadStatus.UnexpectedError, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[TestMethod]
|
||||||
|
public void MustInitializeSessionConfiguration()
|
||||||
{
|
{
|
||||||
var appConfig = sut.InitializeAppConfig();
|
var appConfig = sut.InitializeAppConfig();
|
||||||
var configuration = sut.InitializeSessionConfiguration();
|
var configuration = sut.InitializeSessionConfiguration();
|
||||||
|
@ -44,7 +282,7 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustCorrectlyUpdateAppConfig()
|
public void MustUpdateAppConfig()
|
||||||
{
|
{
|
||||||
var appConfig = sut.InitializeAppConfig();
|
var appConfig = sut.InitializeAppConfig();
|
||||||
var clientAddress = appConfig.ClientAddress;
|
var clientAddress = appConfig.ClientAddress;
|
||||||
|
@ -64,7 +302,7 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustCorrectlyUpdateSessionConfiguration()
|
public void MustUpdateSessionConfiguration()
|
||||||
{
|
{
|
||||||
var appConfig = sut.InitializeAppConfig();
|
var appConfig = sut.InitializeAppConfig();
|
||||||
var firstSession = sut.InitializeSessionConfiguration();
|
var firstSession = sut.InitializeSessionConfiguration();
|
||||||
|
@ -76,5 +314,16 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
Assert.AreNotEqual(secondSession.Id, thirdSession.Id);
|
Assert.AreNotEqual(secondSession.Id, thirdSession.Id);
|
||||||
Assert.AreNotEqual(secondSession.StartupToken, thirdSession.StartupToken);
|
Assert.AreNotEqual(secondSession.StartupToken, thirdSession.StartupToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void RegisterModules()
|
||||||
|
{
|
||||||
|
sut.Register(binaryParser.Object);
|
||||||
|
sut.Register(binarySerializer.Object);
|
||||||
|
sut.Register(fileLoader.Object);
|
||||||
|
sut.Register(fileSaver.Object);
|
||||||
|
sut.Register(networkLoader.Object);
|
||||||
|
sut.Register(xmlParser.Object);
|
||||||
|
sut.Register(xmlSerializer.Object);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2019 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 System.Security.Cryptography.X509Certificates;
|
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.ConfigurationData
|
|
||||||
{
|
|
||||||
internal class CertificateImporter
|
|
||||||
{
|
|
||||||
private ILogger logger;
|
|
||||||
|
|
||||||
internal CertificateImporter(ILogger logger)
|
|
||||||
{
|
|
||||||
this.logger = logger;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ExtractAndImportIdentities(IDictionary<string, object> data)
|
|
||||||
{
|
|
||||||
const int IDENTITY_CERTIFICATE = 1;
|
|
||||||
var hasCertificates = data.TryGetValue(Keys.Network.Certificates.EmbeddedCertificates, out var value);
|
|
||||||
|
|
||||||
if (hasCertificates && value is IList<IDictionary<string, object>> certificates)
|
|
||||||
{
|
|
||||||
var toRemove = new List<IDictionary<string, object>>();
|
|
||||||
|
|
||||||
foreach (var certificate in certificates)
|
|
||||||
{
|
|
||||||
var hasData = certificate.TryGetValue(Keys.Network.Certificates.CertificateData, out var dataValue);
|
|
||||||
var hasType = certificate.TryGetValue(Keys.Network.Certificates.CertificateType, out var typeValue);
|
|
||||||
var isIdentity = typeValue is int type && type == IDENTITY_CERTIFICATE;
|
|
||||||
|
|
||||||
if (hasData && hasType && isIdentity && dataValue is byte[] certificateData)
|
|
||||||
{
|
|
||||||
ImportIdentityCertificate(certificateData, new X509Store(StoreLocation.CurrentUser));
|
|
||||||
ImportIdentityCertificate(certificateData, new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine));
|
|
||||||
|
|
||||||
toRemove.Add(certificate);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
toRemove.ForEach(c => certificates.Remove(c));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void ImportIdentityCertificate(byte[] certificateData, X509Store store)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var certificate = new X509Certificate2();
|
|
||||||
|
|
||||||
certificate.Import(certificateData, "Di𝈭l𝈖Ch𝈒aht𝈁aHai1972", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
|
|
||||||
|
|
||||||
store.Open(OpenFlags.ReadWrite);
|
|
||||||
store.Add(certificate);
|
|
||||||
|
|
||||||
logger.Info($"Successfully imported identity certificate into {store.Location}.{store.Name}.");
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
logger.Error($"Failed to import identity certificate into {store.Location}.{store.Name}!", e);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
store.Close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,7 +22,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
public class ConfigurationRepository : IConfigurationRepository
|
public class ConfigurationRepository : IConfigurationRepository
|
||||||
{
|
{
|
||||||
private CertificateImporter certificateImporter;
|
private ICertificateStore certificateStore;
|
||||||
private IList<IDataParser> dataParsers;
|
private IList<IDataParser> dataParsers;
|
||||||
private IList<IDataSerializer> dataSerializers;
|
private IList<IDataSerializer> dataSerializers;
|
||||||
private DataMapper dataMapper;
|
private DataMapper dataMapper;
|
||||||
|
@ -33,6 +33,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
private IList<IResourceSaver> resourceSavers;
|
private IList<IResourceSaver> resourceSavers;
|
||||||
|
|
||||||
public ConfigurationRepository(
|
public ConfigurationRepository(
|
||||||
|
ICertificateStore certificateStore,
|
||||||
IHashAlgorithm hashAlgorithm,
|
IHashAlgorithm hashAlgorithm,
|
||||||
IModuleLogger logger,
|
IModuleLogger logger,
|
||||||
string executablePath,
|
string executablePath,
|
||||||
|
@ -40,10 +41,10 @@ namespace SafeExamBrowser.Configuration
|
||||||
string programTitle,
|
string programTitle,
|
||||||
string programVersion)
|
string programVersion)
|
||||||
{
|
{
|
||||||
|
this.certificateStore = certificateStore;
|
||||||
this.hashAlgorithm = hashAlgorithm;
|
this.hashAlgorithm = hashAlgorithm;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
|
|
||||||
certificateImporter = new CertificateImporter(logger.CloneFor(nameof(CertificateImporter)));
|
|
||||||
dataParsers = new List<IDataParser>();
|
dataParsers = new List<IDataParser>();
|
||||||
dataSerializers = new List<IDataSerializer>();
|
dataSerializers = new List<IDataSerializer>();
|
||||||
dataMapper = new DataMapper();
|
dataMapper = new DataMapper();
|
||||||
|
@ -64,7 +65,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
TryParseData(data, out var encryption, out var format, out var rawData, password);
|
TryParseData(data, out var encryption, out var format, out var rawData, password);
|
||||||
|
|
||||||
certificateImporter.ExtractAndImportIdentities(rawData);
|
certificateStore.ExtractAndImportIdentities(rawData);
|
||||||
encryption = DetermineEncryptionForClientConfiguration(rawData, encryption);
|
encryption = DetermineEncryptionForClientConfiguration(rawData, encryption);
|
||||||
|
|
||||||
var status = TrySerializeData(rawData, format, out var serialized, encryption);
|
var status = TrySerializeData(rawData, format, out var serialized, encryption);
|
||||||
|
|
|
@ -6,15 +6,21 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Security.Cryptography;
|
using System.Security.Cryptography;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
using SafeExamBrowser.Configuration.ConfigurationData;
|
||||||
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.Cryptography
|
namespace SafeExamBrowser.Configuration.Cryptography
|
||||||
{
|
{
|
||||||
public class CertificateStore : ICertificateStore
|
public class CertificateStore : ICertificateStore
|
||||||
{
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
private readonly X509Store[] stores = new[]
|
private readonly X509Store[] stores = new[]
|
||||||
{
|
{
|
||||||
new X509Store(StoreLocation.CurrentUser),
|
new X509Store(StoreLocation.CurrentUser),
|
||||||
|
@ -22,6 +28,11 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
||||||
new X509Store(StoreName.TrustedPeople)
|
new X509Store(StoreName.TrustedPeople)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
public CertificateStore(ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
public bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate)
|
public bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate)
|
||||||
{
|
{
|
||||||
certificate = default(X509Certificate2);
|
certificate = default(X509Certificate2);
|
||||||
|
@ -56,5 +67,56 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ExtractAndImportIdentities(IDictionary<string, object> data)
|
||||||
|
{
|
||||||
|
const int IDENTITY_CERTIFICATE = 1;
|
||||||
|
var hasCertificates = data.TryGetValue(Keys.Network.Certificates.EmbeddedCertificates, out var value);
|
||||||
|
|
||||||
|
if (hasCertificates && value is IList<IDictionary<string, object>> certificates)
|
||||||
|
{
|
||||||
|
var toRemove = new List<IDictionary<string, object>>();
|
||||||
|
|
||||||
|
foreach (var certificate in certificates)
|
||||||
|
{
|
||||||
|
var hasData = certificate.TryGetValue(Keys.Network.Certificates.CertificateData, out var dataValue);
|
||||||
|
var hasType = certificate.TryGetValue(Keys.Network.Certificates.CertificateType, out var typeValue);
|
||||||
|
var isIdentity = typeValue is int type && type == IDENTITY_CERTIFICATE;
|
||||||
|
|
||||||
|
if (hasData && hasType && isIdentity && dataValue is byte[] certificateData)
|
||||||
|
{
|
||||||
|
ImportIdentityCertificate(certificateData, new X509Store(StoreLocation.CurrentUser));
|
||||||
|
ImportIdentityCertificate(certificateData, new X509Store(StoreName.TrustedPeople, StoreLocation.LocalMachine));
|
||||||
|
|
||||||
|
toRemove.Add(certificate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
toRemove.ForEach(c => certificates.Remove(c));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ImportIdentityCertificate(byte[] certificateData, X509Store store)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var certificate = new X509Certificate2();
|
||||||
|
|
||||||
|
certificate.Import(certificateData, "Di𝈭l𝈖Ch𝈒aht𝈁aHai1972", X509KeyStorageFlags.UserKeySet | X509KeyStorageFlags.PersistKeySet);
|
||||||
|
|
||||||
|
store.Open(OpenFlags.ReadWrite);
|
||||||
|
store.Add(certificate);
|
||||||
|
|
||||||
|
logger.Info($"Successfully imported identity certificate into {store.Location}.{store.Name}.");
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to import identity certificate into {store.Location}.{store.Name}!", e);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
store.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,6 @@
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ConfigurationData\CertificateImporter.cs" />
|
|
||||||
<Compile Include="ConfigurationData\Keys.cs" />
|
<Compile Include="ConfigurationData\Keys.cs" />
|
||||||
<Compile Include="ConfigurationData\DataValues.cs" />
|
<Compile Include="ConfigurationData\DataValues.cs" />
|
||||||
<Compile Include="Cryptography\CertificateStore.cs" />
|
<Compile Include="Cryptography\CertificateStore.cs" />
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Security.Cryptography.X509Certificates;
|
using System.Security.Cryptography.X509Certificates;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Provides functionality to load certificates installed on the computer.
|
/// Provides functionality related to certificates installed on the computer.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface ICertificateStore
|
public interface ICertificateStore
|
||||||
{
|
{
|
||||||
|
@ -20,5 +21,10 @@ namespace SafeExamBrowser.Contracts.Configuration.Cryptography
|
||||||
/// Returns <c>true</c> if the certificate was found, otherwise <c>false</c>.
|
/// Returns <c>true</c> if the certificate was found, otherwise <c>false</c>.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate);
|
bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Extracts all identity certificates from the given configuration data and installs them on the computer.
|
||||||
|
/// </summary>
|
||||||
|
void ExtractAndImportIdentities(IDictionary<string, object> data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -116,15 +116,17 @@ namespace SafeExamBrowser.Runtime
|
||||||
var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright;
|
var programCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright;
|
||||||
var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
var programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
|
||||||
var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
|
||||||
|
|
||||||
|
var certificateStore = new CertificateStore(ModuleLogger(nameof(CertificateStore)));
|
||||||
var compressor = new GZipCompressor(ModuleLogger(nameof(GZipCompressor)));
|
var compressor = new GZipCompressor(ModuleLogger(nameof(GZipCompressor)));
|
||||||
var passwordEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption)));
|
var passwordEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption)));
|
||||||
var publicKeyEncryption = new PublicKeyEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeyEncryption)));
|
var publicKeyEncryption = new PublicKeyEncryption(certificateStore, ModuleLogger(nameof(PublicKeyEncryption)));
|
||||||
var symmetricEncryption = new PublicKeySymmetricEncryption(new 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(ModuleLogger(nameof(XmlParser)));
|
||||||
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
|
var xmlSerializer = new XmlSerializer(ModuleLogger(nameof(XmlSerializer)));
|
||||||
|
|
||||||
configuration = new ConfigurationRepository(new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion);
|
configuration = new ConfigurationRepository(certificateStore, new HashAlgorithm(), repositoryLogger, executable.Location, programCopyright, programTitle, programVersion);
|
||||||
appConfig = configuration.InitializeAppConfig();
|
appConfig = configuration.InitializeAppConfig();
|
||||||
|
|
||||||
configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser));
|
configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser));
|
||||||
|
|
Loading…
Reference in a new issue