diff --git a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs index 518f2f9c..b556b206 100644 --- a/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs +++ b/SafeExamBrowser.Client.UnitTests/ClientControllerTests.cs @@ -535,7 +535,7 @@ namespace SafeExamBrowser.Client.UnitTests var filename = "filepath.seb"; var args = new DownloadEventArgs(); - appConfig.DownloadDirectory = @"C:\Folder\Does\Not\Exist"; + appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist"; settings.ConfigurationMode = ConfigurationMode.ConfigureClient; messageBox.Setup(m => m.Show( It.IsAny(), @@ -562,7 +562,7 @@ namespace SafeExamBrowser.Client.UnitTests var filename = "filepath.seb"; var args = new DownloadEventArgs(); - appConfig.DownloadDirectory = @"C:\Folder\Does\Not\Exist"; + appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist"; settings.ConfigurationMode = ConfigurationMode.ConfigureClient; messageBox.Setup(m => m.Show( It.IsAny(), @@ -586,7 +586,7 @@ namespace SafeExamBrowser.Client.UnitTests var filename = "filepath.seb"; var args = new DownloadEventArgs(); - appConfig.DownloadDirectory = @"C:\Folder\Does\Not\Exist"; + appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist"; settings.ConfigurationMode = ConfigurationMode.ConfigureClient; messageBox.Setup(m => m.Show( It.IsAny(), diff --git a/SafeExamBrowser.Client/ClientController.cs b/SafeExamBrowser.Client/ClientController.cs index 4bf9a03f..a1a532c7 100644 --- a/SafeExamBrowser.Client/ClientController.cs +++ b/SafeExamBrowser.Client/ClientController.cs @@ -337,7 +337,7 @@ namespace SafeExamBrowser.Client { args.AllowDownload = true; args.Callback = Browser_ConfigurationDownloadFinished; - args.DownloadPath = Path.Combine(context.AppConfig.DownloadDirectory, fileName); + args.DownloadPath = Path.Combine(context.AppConfig.TemporaryDirectory, fileName); logger.Info($"Allowed download request for configuration file '{fileName}'."); } else diff --git a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs index 1678db59..df81f47b 100644 --- a/SafeExamBrowser.Configuration.Contracts/AppConfig.cs +++ b/SafeExamBrowser.Configuration.Contracts/AppConfig.cs @@ -91,11 +91,6 @@ namespace SafeExamBrowser.Configuration.Contracts /// public string ConfigurationFileExtension { get; set; } - /// - /// The default directory for file downloads. - /// - public string DownloadDirectory { get; set; } - /// /// The build version of the application. /// @@ -161,6 +156,11 @@ namespace SafeExamBrowser.Configuration.Contracts /// public string ServiceLogFilePath { get; set; } + /// + /// The directory to be used for temporary application data. + /// + public string TemporaryDirectory { get; set; } + /// /// Creates a shallow clone. /// diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index 1621d6d4..b21dcd16 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -68,7 +68,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData appConfig.ClientExecutablePath = Path.Combine(Path.GetDirectoryName(executablePath), $"{nameof(SafeExamBrowser)}.Client.exe"); appConfig.ClientLogFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Client.log"); appConfig.ConfigurationFileExtension = ".seb"; - appConfig.DownloadDirectory = Path.Combine(appDataLocalFolder, "Downloads"); appConfig.ProgramBuildVersion = programBuild; appConfig.ProgramCopyright = programCopyright; appConfig.ProgramDataFilePath = Path.Combine(programDataFolder, DEFAULT_CONFIGURATION_NAME); @@ -82,6 +81,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData appConfig.ServiceAddress = $"{AppConfig.BASE_ADDRESS}/service"; appConfig.ServiceEventName = $@"Global\{nameof(SafeExamBrowser)}-{Guid.NewGuid()}"; appConfig.ServiceLogFilePath = Path.Combine(logFolder, $"{logFilePrefix}_Service.log"); + appConfig.TemporaryDirectory = Path.Combine(appDataLocalFolder, "Temp"); return appConfig; } @@ -123,8 +123,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData settings.Browser.AdditionalWindow.RelativeWidth = 50; settings.Browser.AdditionalWindow.ShowReloadWarning = false; settings.Browser.AllowConfigurationDownloads = true; + settings.Browser.AllowCustomDownloadLocation = false; settings.Browser.AllowDownloads = true; settings.Browser.AllowPageZoom = true; + settings.Browser.AllowUploads = true; settings.Browser.MainWindow.AllowAddressBar = false; settings.Browser.MainWindow.AllowBackwardNavigation = false; settings.Browser.MainWindow.AllowDeveloperConsole = false; diff --git a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs index 6e3d8c8b..905ade50 100644 --- a/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs +++ b/SafeExamBrowser.Runtime.UnitTests/Operations/ConfigurationOperationTests.cs @@ -18,6 +18,7 @@ using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Operations; using SafeExamBrowser.Runtime.Operations.Events; using SafeExamBrowser.Settings; +using SafeExamBrowser.SystemComponents.Contracts; namespace SafeExamBrowser.Runtime.UnitTests.Operations { @@ -27,6 +28,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations private const string FILE_NAME = "SebClientSettings.seb"; private AppConfig appConfig; + private Mock fileSystem; private Mock hashAlgorithm; private Mock logger; private Mock repository; @@ -38,6 +40,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Initialize() { appConfig = new AppConfig(); + fileSystem = new Mock(); hashAlgorithm = new Mock(); logger = new Mock(); repository = new Mock(); @@ -65,7 +68,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); var resource = new Uri(url); @@ -83,7 +86,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(location)), out settings, It.IsAny()), Times.Once); @@ -99,7 +102,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations appConfig.AppDataFilePath = location; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(location)), out settings, It.IsAny()), Times.Once); @@ -115,7 +118,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations nextSession.Settings = settings; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.LoadWithBrowser); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); Assert.AreEqual(url, settings.Browser.StartUrl); @@ -129,7 +132,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); repository.Verify(r => r.LoadDefaultSettings(), Times.Once); @@ -149,7 +152,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.IsAny(), It.IsAny())).Returns(SaveStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is ConfigurationCompletedEventArgs c) @@ -173,7 +176,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.IsAny(), It.IsAny())).Returns(SaveStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is ConfigurationCompletedEventArgs c) @@ -198,7 +201,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.IsAny(), It.IsAny())).Returns(SaveStatus.UnexpectedError); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is ClientConfigurationErrorMessageArgs) @@ -221,7 +224,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations settings.ConfigurationMode = ConfigurationMode.Exam; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is ConfigurationCompletedEventArgs c) @@ -243,14 +246,14 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); result = sut.Perform(); repository.Verify(r => r.LoadDefaultSettings(), Times.Once); Assert.AreEqual(OperationResult.Success, result); Assert.AreSame(defaultSettings, nextSession.Settings); - sut = new ConfigurationOperation(new string[] { }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + sut = new ConfigurationOperation(new string[] { }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); result = sut.Perform(); repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2)); @@ -262,7 +265,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations public void Perform_MustNotFailWithInvalidUri() { var uri = @"an/invalid\uri.'*%yolo/()你好"; - var sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); Assert.AreEqual(OperationResult.Success, result); @@ -284,7 +287,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.Is(u => u.LocalPath.Contains(FILE_NAME)), out localSettings, It.IsAny())).Returns(LoadStatus.Success); nextSession.Settings = settings; - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator) @@ -309,7 +312,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.Settings) @@ -343,7 +346,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.Is(u => u.AbsoluteUri == url), out nextSettings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.IsAny(), It.IsAny())).Returns(SaveStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator) @@ -374,7 +377,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.Is(u => u.AbsoluteUri == url), out nextSettings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.IsAny(), It.IsAny())).Returns(SaveStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs) @@ -400,7 +403,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.Is(p => p.Password == password))).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p) @@ -437,7 +440,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations .Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.Is(p => p.IsHash == true && p.Password == settings.Security.AdminPasswordHash))) .Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Perform(); repository.Verify(r => r.TryLoadSettings(It.IsAny(), out settings, It.Is(p => p.Password == settings.Security.AdminPasswordHash)), Times.AtLeastOnce); @@ -462,7 +465,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out currentSettings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.TryLoadSettings(It.Is(u => u.AbsoluteUri == url), out nextSettings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p && p.Purpose == PasswordRequestPurpose.LocalAdministrator) @@ -486,7 +489,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); - var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p) @@ -512,9 +515,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations sessionContext.ReconfigurationFilePath = resource.LocalPath; repository.Setup(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Repeat(); + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Once); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny()), Times.AtLeastOnce); repository.Verify(r => r.ConfigureClientWith(It.Is(u => u.Equals(resource)), It.IsAny()), Times.Never); @@ -534,15 +538,45 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations repository.Setup(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny())).Returns(LoadStatus.Success); repository.Setup(r => r.ConfigureClientWith(It.Is(u => u.Equals(resource)), It.IsAny())).Returns(SaveStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Repeat(); + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Once); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny()), Times.AtLeastOnce); repository.Verify(r => r.ConfigureClientWith(It.Is(u => u.Equals(resource)), It.IsAny()), Times.Once); Assert.AreEqual(OperationResult.Success, result); } + [TestMethod] + public void Repeat_MustDeleteTemporaryFileAfterClientConfiguration() + { + var currentSettings = new AppSettings(); + var location = Path.GetDirectoryName(GetType().Assembly.Location); + var resource = new Uri(Path.Combine(location, nameof(Operations), "Testdata", FILE_NAME)); + var settings = new AppSettings { ConfigurationMode = ConfigurationMode.ConfigureClient }; + var delete = 0; + var configure = 0; + var order = 0; + + currentSession.Settings = currentSettings; + sessionContext.ReconfigurationFilePath = resource.LocalPath; + fileSystem.Setup(f => f.Delete(It.IsAny())).Callback(() => delete = ++order); + repository.Setup(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny())).Returns(LoadStatus.Success); + repository.Setup(r => r.ConfigureClientWith(It.Is(u => u.Equals(resource)), It.IsAny())).Returns(SaveStatus.Success).Callback(() => configure = ++order); + + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var result = sut.Repeat(); + + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Once); + repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny()), Times.AtLeastOnce); + repository.Verify(r => r.ConfigureClientWith(It.Is(u => u.Equals(resource)), It.IsAny()), Times.Once); + + Assert.AreEqual(OperationResult.Success, result); + Assert.AreEqual(1, configure); + Assert.AreEqual(2, delete); + } + [TestMethod] public void Repeat_MustFailWithInvalidUri() { @@ -552,15 +586,17 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations sessionContext.ReconfigurationFilePath = null; repository.Setup(r => r.TryLoadSettings(It.IsAny(), out settings, It.IsAny())).Returns(LoadStatus.Success); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Repeat(); + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Never); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny()), Times.Never); Assert.AreEqual(OperationResult.Failed, result); sessionContext.ReconfigurationFilePath = resource.LocalPath; result = sut.Repeat(); + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Never); repository.Verify(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny()), Times.Never); Assert.AreEqual(OperationResult.Failed, result); } @@ -577,7 +613,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations sessionContext.ReconfigurationFilePath = resource.LocalPath; repository.Setup(r => r.TryLoadSettings(It.Is(u => u.Equals(resource)), out settings, It.IsAny())).Returns(LoadStatus.PasswordNeeded); - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); sut.ActionRequired += args => { if (args is PasswordRequiredEventArgs p) @@ -588,15 +624,17 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations var result = sut.Repeat(); + fileSystem.Verify(f => f.Delete(It.Is(s => s == resource.LocalPath)), Times.Once); Assert.AreEqual(OperationResult.Aborted, result); } [TestMethod] public void Revert_MustDoNothing() { - var sut = new ConfigurationOperation(null, repository.Object, hashAlgorithm.Object, logger.Object, sessionContext); + var sut = new ConfigurationOperation(null, repository.Object, fileSystem.Object, hashAlgorithm.Object, logger.Object, sessionContext); var result = sut.Revert(); + fileSystem.VerifyNoOtherCalls(); hashAlgorithm.VerifyNoOtherCalls(); repository.VerifyNoOtherCalls(); diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 3f000051..bbcfe4e9 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -83,7 +83,7 @@ namespace SafeExamBrowser.Runtime bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger)); sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext)); - sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, new HashAlgorithm(), logger, sessionContext)); + sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, new FileSystem(), new HashAlgorithm(), logger, sessionContext)); sessionOperations.Enqueue(new VirtualMachineOperation(vmDetector, logger, sessionContext)); sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo)); sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS)); diff --git a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs index fc862c6f..ae748b20 100644 --- a/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/ConfigurationOperation.cs @@ -17,6 +17,7 @@ using SafeExamBrowser.I18n.Contracts; using SafeExamBrowser.Logging.Contracts; using SafeExamBrowser.Runtime.Operations.Events; using SafeExamBrowser.Settings; +using SafeExamBrowser.SystemComponents.Contracts; namespace SafeExamBrowser.Runtime.Operations { @@ -24,6 +25,7 @@ namespace SafeExamBrowser.Runtime.Operations { private string[] commandLineArgs; private IConfigurationRepository configuration; + private IFileSystem fileSystem; private IHashAlgorithm hashAlgorithm; private ILogger logger; @@ -36,12 +38,14 @@ namespace SafeExamBrowser.Runtime.Operations public ConfigurationOperation( string[] commandLineArgs, IConfigurationRepository configuration, + IFileSystem fileSystem, IHashAlgorithm hashAlgorithm, ILogger logger, SessionContext sessionContext) : base(sessionContext) { this.commandLineArgs = commandLineArgs; this.configuration = configuration; + this.fileSystem = fileSystem; this.hashAlgorithm = hashAlgorithm; this.logger = logger; } @@ -150,19 +154,27 @@ namespace SafeExamBrowser.Runtime.Operations var currentPassword = Context.Current.Settings.Security.AdminPasswordHash; var source = UriSource.Reconfiguration; var status = TryLoadSettings(uri, source, out var passwordParams, out var settings, currentPassword); + var result = OperationResult.Failed; if (status.HasValue) { - return DetermineLoadResult(uri, source, settings, status.Value, passwordParams, currentPassword); + result = DetermineLoadResult(uri, source, settings, status.Value, passwordParams, currentPassword); } else { - return OperationResult.Aborted; + result = OperationResult.Aborted; } + + fileSystem.Delete(uri.LocalPath); + logger.Info($"Deleted temporary configuration file '{uri}'."); + + return result; } private OperationResult DetermineLoadResult(Uri uri, UriSource source, AppSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string)) { + var result = OperationResult.Failed; + if (status == LoadStatus.LoadWithBrowser || status == LoadStatus.Success) { var isNewConfiguration = source == UriSource.CommandLine || source == UriSource.Reconfiguration; @@ -171,20 +183,23 @@ namespace SafeExamBrowser.Runtime.Operations if (status == LoadStatus.LoadWithBrowser) { - return HandleBrowserResource(uri); + result = HandleBrowserResource(uri); } - - if (isNewConfiguration && settings.ConfigurationMode == ConfigurationMode.ConfigureClient) + else if (isNewConfiguration && settings.ConfigurationMode == ConfigurationMode.ConfigureClient) { - return HandleClientConfiguration(uri, passwordParams, currentPassword); + result = HandleClientConfiguration(uri, passwordParams, currentPassword); } - - return OperationResult.Success; + else + { + result = OperationResult.Success; + } + } + else + { + ShowFailureMessage(status, uri); } - ShowFailureMessage(status, uri); - - return OperationResult.Failed; + return result; } private OperationResult HandleBrowserResource(Uri uri) @@ -199,23 +214,18 @@ namespace SafeExamBrowser.Runtime.Operations { var isFirstSession = Context.Current == null; var success = TryConfigureClient(uri, passwordParams, currentPassword); + var result = OperationResult.Failed; - if (success == true) + if (!success.HasValue || (success == true && isFirstSession && AbortAfterClientConfiguration())) { - if (isFirstSession && AbortAfterClientConfiguration()) - { - return OperationResult.Aborted; - } - - return OperationResult.Success; + result = OperationResult.Aborted; + } + else if (success == true) + { + result = OperationResult.Success; } - if (!success.HasValue) - { - return OperationResult.Aborted; - } - - return OperationResult.Failed; + return result; } private LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out AppSettings settings, string currentPassword = default(string)) diff --git a/SafeExamBrowser.Settings/Browser/BrowserSettings.cs b/SafeExamBrowser.Settings/Browser/BrowserSettings.cs index 68c8b93b..2197a8d7 100644 --- a/SafeExamBrowser.Settings/Browser/BrowserSettings.cs +++ b/SafeExamBrowser.Settings/Browser/BrowserSettings.cs @@ -11,7 +11,7 @@ using System; namespace SafeExamBrowser.Settings.Browser { /// - /// Defines all settings for the browser engine. + /// Defines all settings for the integrated browser application. /// [Serializable] public class BrowserSettings diff --git a/SafeExamBrowser.SystemComponents.Contracts/IFileSystem.cs b/SafeExamBrowser.SystemComponents.Contracts/IFileSystem.cs new file mode 100644 index 00000000..beeaab39 --- /dev/null +++ b/SafeExamBrowser.SystemComponents.Contracts/IFileSystem.cs @@ -0,0 +1,21 @@ +/* + * Copyright (c) 2020 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/. + */ + +namespace SafeExamBrowser.SystemComponents.Contracts +{ + /// + /// Provides access to file system operations. + /// + public interface IFileSystem + { + /// + /// Deletes the item at the given path, if it exists. Directories will be completely deleted, including all subdirectories and files. + /// + void Delete(string path); + } +} diff --git a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj index d4a6a7ca..4e4c33de 100644 --- a/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj +++ b/SafeExamBrowser.SystemComponents.Contracts/SafeExamBrowser.SystemComponents.Contracts.csproj @@ -56,6 +56,7 @@ + diff --git a/SafeExamBrowser.SystemComponents/FileSystem.cs b/SafeExamBrowser.SystemComponents/FileSystem.cs new file mode 100644 index 00000000..8b00021d --- /dev/null +++ b/SafeExamBrowser.SystemComponents/FileSystem.cs @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020 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.IO; +using SafeExamBrowser.SystemComponents.Contracts; + +namespace SafeExamBrowser.SystemComponents +{ + public class FileSystem : IFileSystem + { + public void Delete(string path) + { + if (File.Exists(path)) + { + File.Delete(path); + } + + if (Directory.Exists(path)) + { + Directory.Delete(path, true); + } + } + } +} diff --git a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj index a4414c7f..25624851 100644 --- a/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj +++ b/SafeExamBrowser.SystemComponents/SafeExamBrowser.SystemComponents.csproj @@ -64,6 +64,7 @@ +