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…
	
	Add table
		
		Reference in a new issue
	
	 dbuechel
						dbuechel