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.Collections.Generic; | ||||
| using System.IO; | ||||
| using System.Reflection; | ||||
| using Microsoft.VisualStudio.TestTools.UnitTesting; | ||||
| using Moq; | ||||
| using SafeExamBrowser.Configuration.ConfigurationData; | ||||
| using SafeExamBrowser.Contracts.Configuration; | ||||
| using SafeExamBrowser.Contracts.Configuration.Cryptography; | ||||
| using SafeExamBrowser.Contracts.Configuration.DataFormats; | ||||
| using SafeExamBrowser.Contracts.Configuration.DataResources; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Configuration.UnitTests | ||||
|  | @ -20,19 +25,252 @@ namespace SafeExamBrowser.Configuration.UnitTests | |||
| 	public class ConfigurationRepositoryTests | ||||
| 	{ | ||||
| 		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] | ||||
| 		public void Initialize() | ||||
| 		{ | ||||
| 			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] | ||||
| 		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 configuration = sut.InitializeSessionConfiguration(); | ||||
|  | @ -44,7 +282,7 @@ namespace SafeExamBrowser.Configuration.UnitTests | |||
| 		} | ||||
| 
 | ||||
| 		[TestMethod] | ||||
| 		public void MustCorrectlyUpdateAppConfig() | ||||
| 		public void MustUpdateAppConfig() | ||||
| 		{ | ||||
| 			var appConfig = sut.InitializeAppConfig(); | ||||
| 			var clientAddress = appConfig.ClientAddress; | ||||
|  | @ -64,7 +302,7 @@ namespace SafeExamBrowser.Configuration.UnitTests | |||
| 		} | ||||
| 
 | ||||
| 		[TestMethod] | ||||
| 		public void MustCorrectlyUpdateSessionConfiguration() | ||||
| 		public void MustUpdateSessionConfiguration() | ||||
| 		{ | ||||
| 			var appConfig = sut.InitializeAppConfig(); | ||||
| 			var firstSession = sut.InitializeSessionConfiguration(); | ||||
|  | @ -76,5 +314,16 @@ namespace SafeExamBrowser.Configuration.UnitTests | |||
| 			Assert.AreNotEqual(secondSession.Id, thirdSession.Id); | ||||
| 			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 | ||||
| 	{ | ||||
| 		private CertificateImporter certificateImporter; | ||||
| 		private ICertificateStore certificateStore; | ||||
| 		private IList<IDataParser> dataParsers; | ||||
| 		private IList<IDataSerializer> dataSerializers; | ||||
| 		private DataMapper dataMapper; | ||||
|  | @ -33,6 +33,7 @@ namespace SafeExamBrowser.Configuration | |||
| 		private IList<IResourceSaver> resourceSavers; | ||||
| 
 | ||||
| 		public ConfigurationRepository( | ||||
| 			ICertificateStore certificateStore, | ||||
| 			IHashAlgorithm hashAlgorithm, | ||||
| 			IModuleLogger logger, | ||||
| 			string executablePath, | ||||
|  | @ -40,10 +41,10 @@ namespace SafeExamBrowser.Configuration | |||
| 			string programTitle, | ||||
| 			string programVersion) | ||||
| 		{ | ||||
| 			this.certificateStore = certificateStore; | ||||
| 			this.hashAlgorithm = hashAlgorithm; | ||||
| 			this.logger = logger; | ||||
| 
 | ||||
| 			certificateImporter = new CertificateImporter(logger.CloneFor(nameof(CertificateImporter))); | ||||
| 			dataParsers = new List<IDataParser>(); | ||||
| 			dataSerializers = new List<IDataSerializer>(); | ||||
| 			dataMapper = new DataMapper(); | ||||
|  | @ -64,7 +65,7 @@ namespace SafeExamBrowser.Configuration | |||
| 				{ | ||||
| 					TryParseData(data, out var encryption, out var format, out var rawData, password); | ||||
| 
 | ||||
| 					certificateImporter.ExtractAndImportIdentities(rawData); | ||||
| 					certificateStore.ExtractAndImportIdentities(rawData); | ||||
| 					encryption = DetermineEncryptionForClientConfiguration(rawData, 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/. | ||||
|  */ | ||||
| 
 | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Linq; | ||||
| using System.Security.Cryptography; | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
| using SafeExamBrowser.Configuration.ConfigurationData; | ||||
| using SafeExamBrowser.Contracts.Configuration.Cryptography; | ||||
| using SafeExamBrowser.Contracts.Logging; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Configuration.Cryptography | ||||
| { | ||||
| 	public class CertificateStore : ICertificateStore | ||||
| 	{ | ||||
| 		private ILogger logger; | ||||
| 
 | ||||
| 		private readonly X509Store[] stores = new[] | ||||
| 		{ | ||||
| 			new X509Store(StoreLocation.CurrentUser), | ||||
|  | @ -22,6 +28,11 @@ namespace SafeExamBrowser.Configuration.Cryptography | |||
| 			new X509Store(StoreName.TrustedPeople) | ||||
| 		}; | ||||
| 
 | ||||
| 		public CertificateStore(ILogger logger) | ||||
| 		{ | ||||
| 			this.logger = logger; | ||||
| 		} | ||||
| 
 | ||||
| 		public bool TryGetCertificateWith(byte[] keyHash, out X509Certificate2 certificate) | ||||
| 		{ | ||||
| 			certificate = default(X509Certificate2); | ||||
|  | @ -56,5 +67,56 @@ namespace SafeExamBrowser.Configuration.Cryptography | |||
| 
 | ||||
| 			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" /> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <Compile Include="ConfigurationData\CertificateImporter.cs" /> | ||||
|     <Compile Include="ConfigurationData\Keys.cs" /> | ||||
|     <Compile Include="ConfigurationData\DataValues.cs" /> | ||||
|     <Compile Include="Cryptography\CertificateStore.cs" /> | ||||
|  |  | |||
|  | @ -6,12 +6,13 @@ | |||
|  * file, You can obtain one at http://mozilla.org/MPL/2.0/. | ||||
|  */ | ||||
| 
 | ||||
| using System.Collections.Generic; | ||||
| using System.Security.Cryptography.X509Certificates; | ||||
| 
 | ||||
| namespace SafeExamBrowser.Contracts.Configuration.Cryptography | ||||
| { | ||||
| 	/// <summary> | ||||
| 	/// Provides functionality to load certificates installed on the computer. | ||||
| 	/// Provides functionality related to certificates installed on the computer. | ||||
| 	/// </summary> | ||||
| 	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>. | ||||
| 		/// </summary> | ||||
| 		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 programTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title; | ||||
| 			var programVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion; | ||||
| 
 | ||||
| 			var certificateStore = new CertificateStore(ModuleLogger(nameof(CertificateStore))); | ||||
| 			var compressor = new GZipCompressor(ModuleLogger(nameof(GZipCompressor))); | ||||
| 			var passwordEncryption = new PasswordEncryption(ModuleLogger(nameof(PasswordEncryption))); | ||||
| 			var publicKeyEncryption = new PublicKeyEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeyEncryption))); | ||||
| 			var symmetricEncryption = new PublicKeySymmetricEncryption(new CertificateStore(), ModuleLogger(nameof(PublicKeySymmetricEncryption)), passwordEncryption); | ||||
| 			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 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(); | ||||
| 
 | ||||
| 			configuration.Register(new BinaryParser(compressor, new HashAlgorithm(), ModuleLogger(nameof(BinaryParser)), passwordEncryption, publicKeyEncryption, symmetricEncryption, xmlParser)); | ||||
|  |  | |||
		Loading…
	
	Add table
		
		Reference in a new issue
	
	 dbuechel
						dbuechel