SEBWIN-221: Implemented scaffolding for loading and parsing of configuration resources.
This commit is contained in:
parent
b4f468a2b4
commit
902b0c2b3b
33 changed files with 710 additions and 174 deletions
|
@ -103,9 +103,17 @@ namespace SafeExamBrowser.Browser
|
||||||
|
|
||||||
private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
|
private void DownloadHandler_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
|
||||||
{
|
{
|
||||||
args.BrowserWindow = window;
|
if (settings.AllowConfigurationDownloads)
|
||||||
|
{
|
||||||
|
args.BrowserWindow = window;
|
||||||
|
logger.Debug($"Forwarding download request for configuration file '{fileName}'.");
|
||||||
|
|
||||||
ConfigurationDownloadRequested?.Invoke(fileName, args);
|
ConfigurationDownloadRequested?.Invoke(fileName, args);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
logger.Debug($"Discarded download request for configuration file '{fileName}'.");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void Window_AddressChanged(string address)
|
private void Window_AddressChanged(string address)
|
||||||
|
|
|
@ -31,11 +31,11 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
|
||||||
if (uri.Scheme == appConfig.SebUriScheme)
|
if (uri.Scheme == appConfig.SebUriScheme)
|
||||||
{
|
{
|
||||||
request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.ToString();
|
request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.Uri.AbsoluteUri;
|
||||||
}
|
}
|
||||||
else if (uri.Scheme == appConfig.SebUriSchemeSecure)
|
else if (uri.Scheme == appConfig.SebUriSchemeSecure)
|
||||||
{
|
{
|
||||||
request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.ToString();
|
request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.Uri.AbsoluteUri;
|
||||||
}
|
}
|
||||||
|
|
||||||
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
|
||||||
|
|
|
@ -206,7 +206,7 @@ namespace SafeExamBrowser.Client
|
||||||
{
|
{
|
||||||
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
|
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
|
||||||
{
|
{
|
||||||
logger.Debug($"Received download request for configuration file '{fileName}'. Asking user to confirm the reconfiguration...");
|
logger.Info($"Received download request for configuration file '{fileName}'. Asking user to confirm the reconfiguration...");
|
||||||
|
|
||||||
var message = TextKey.MessageBox_ReconfigurationQuestion;
|
var message = TextKey.MessageBox_ReconfigurationQuestion;
|
||||||
var title = TextKey.MessageBox_ReconfigurationQuestionTitle;
|
var title = TextKey.MessageBox_ReconfigurationQuestionTitle;
|
||||||
|
|
|
@ -9,7 +9,9 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||||
|
using Moq;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration.UnitTests
|
namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
{
|
{
|
||||||
|
@ -22,8 +24,9 @@ namespace SafeExamBrowser.Configuration.UnitTests
|
||||||
public void Initialize()
|
public void Initialize()
|
||||||
{
|
{
|
||||||
var executablePath = Assembly.GetExecutingAssembly().Location;
|
var executablePath = Assembly.GetExecutingAssembly().Location;
|
||||||
|
var logger = new Mock<ILogger>();
|
||||||
|
|
||||||
sut = new ConfigurationRepository(executablePath, string.Empty, string.Empty, string.Empty);
|
sut = new ConfigurationRepository(logger.Object, executablePath, string.Empty, string.Empty, string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
|
|
@ -7,7 +7,11 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
|
using System.IO.Compression;
|
||||||
|
using System.Linq;
|
||||||
|
using SafeExamBrowser.Configuration.DataFormats;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
using SafeExamBrowser.Contracts.Logging;
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
@ -24,9 +28,16 @@ namespace SafeExamBrowser.Configuration
|
||||||
private readonly string programVersion;
|
private readonly string programVersion;
|
||||||
|
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
|
private IList<IDataFormat> dataFormats;
|
||||||
|
private ILogger logger;
|
||||||
|
private IList<IResourceLoader> resourceLoaders;
|
||||||
|
|
||||||
public ConfigurationRepository(string executablePath, string programCopyright, string programTitle, string programVersion)
|
public ConfigurationRepository(ILogger logger, string executablePath, string programCopyright, string programTitle, string programVersion)
|
||||||
{
|
{
|
||||||
|
dataFormats = new List<IDataFormat>();
|
||||||
|
resourceLoaders = new List<IResourceLoader>();
|
||||||
|
|
||||||
|
this.logger = logger;
|
||||||
this.executablePath = executablePath ?? string.Empty;
|
this.executablePath = executablePath ?? string.Empty;
|
||||||
this.programCopyright = programCopyright ?? string.Empty;
|
this.programCopyright = programCopyright ?? string.Empty;
|
||||||
this.programTitle = programTitle ?? string.Empty;
|
this.programTitle = programTitle ?? string.Empty;
|
||||||
|
@ -73,38 +84,28 @@ namespace SafeExamBrowser.Configuration
|
||||||
|
|
||||||
UpdateAppConfig();
|
UpdateAppConfig();
|
||||||
|
|
||||||
configuration.AppConfig = CloneAppConfig();
|
configuration.AppConfig = appConfig.Clone();
|
||||||
configuration.Id = Guid.NewGuid();
|
configuration.Id = Guid.NewGuid();
|
||||||
configuration.StartupToken = Guid.NewGuid();
|
configuration.StartupToken = Guid.NewGuid();
|
||||||
|
|
||||||
return configuration;
|
return configuration;
|
||||||
}
|
}
|
||||||
|
|
||||||
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string adminPassword = null, string settingsPassword = null)
|
|
||||||
{
|
|
||||||
// TODO: Implement loading mechanism
|
|
||||||
|
|
||||||
settings = LoadDefaultSettings();
|
|
||||||
|
|
||||||
return LoadStatus.Success;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Settings LoadDefaultSettings()
|
public Settings LoadDefaultSettings()
|
||||||
{
|
{
|
||||||
// TODO: Implement default settings
|
|
||||||
|
|
||||||
var settings = new Settings();
|
var settings = new Settings();
|
||||||
|
|
||||||
settings.KioskMode = new Random().Next(10) < 5 ? KioskMode.CreateNewDesktop : KioskMode.DisableExplorerShell;
|
settings.KioskMode = KioskMode.None;
|
||||||
settings.ServicePolicy = ServicePolicy.Optional;
|
settings.ServicePolicy = ServicePolicy.Optional;
|
||||||
|
|
||||||
settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
|
settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
|
||||||
settings.Browser.AllowAddressBar = true;
|
settings.Browser.AllowAddressBar = true;
|
||||||
settings.Browser.AllowBackwardNavigation = true;
|
settings.Browser.AllowBackwardNavigation = true;
|
||||||
|
settings.Browser.AllowConfigurationDownloads = true;
|
||||||
settings.Browser.AllowDeveloperConsole = true;
|
settings.Browser.AllowDeveloperConsole = true;
|
||||||
|
settings.Browser.AllowDownloads = true;
|
||||||
settings.Browser.AllowForwardNavigation = true;
|
settings.Browser.AllowForwardNavigation = true;
|
||||||
settings.Browser.AllowReloading = true;
|
settings.Browser.AllowReloading = true;
|
||||||
settings.Browser.AllowDownloads = true;
|
|
||||||
|
|
||||||
settings.Taskbar.AllowApplicationLog = true;
|
settings.Taskbar.AllowApplicationLog = true;
|
||||||
settings.Taskbar.AllowKeyboardLayout = true;
|
settings.Taskbar.AllowKeyboardLayout = true;
|
||||||
|
@ -113,33 +114,90 @@ namespace SafeExamBrowser.Configuration
|
||||||
return settings;
|
return settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
private AppConfig CloneAppConfig()
|
public void Register(IDataFormat dataFormat)
|
||||||
{
|
{
|
||||||
return new AppConfig
|
dataFormats.Add(dataFormat);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Register(IResourceLoader resourceLoader)
|
||||||
|
{
|
||||||
|
resourceLoaders.Add(resourceLoader);
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, string adminPassword = null, string settingsPassword = null)
|
||||||
|
{
|
||||||
|
settings = default(Settings);
|
||||||
|
|
||||||
|
logger.Info($"Attempting to load '{resource}'...");
|
||||||
|
|
||||||
|
try
|
||||||
{
|
{
|
||||||
AppDataFolder = appConfig.AppDataFolder,
|
var resourceLoader = resourceLoaders.FirstOrDefault(l => l.CanLoad(resource));
|
||||||
ApplicationStartTime = appConfig.ApplicationStartTime,
|
|
||||||
BrowserCachePath = appConfig.BrowserCachePath,
|
if (resourceLoader != null)
|
||||||
BrowserLogFile = appConfig.BrowserLogFile,
|
{
|
||||||
ClientAddress = appConfig.ClientAddress,
|
var data = resourceLoader.Load(resource);
|
||||||
ClientExecutablePath = appConfig.ClientExecutablePath,
|
var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data));
|
||||||
ClientId = appConfig.ClientId,
|
|
||||||
ClientLogFile = appConfig.ClientLogFile,
|
logger.Info($"Successfully loaded {data.Length / 1000.0} KB data from '{resource}' using {resourceLoader.GetType().Name}.");
|
||||||
ConfigurationFileExtension = appConfig.ConfigurationFileExtension,
|
|
||||||
DefaultSettingsFileName = appConfig.DefaultSettingsFileName,
|
if (dataFormat is HtmlFormat)
|
||||||
DownloadDirectory = appConfig.DownloadDirectory,
|
{
|
||||||
LogLevel = appConfig.LogLevel,
|
return HandleHtml(resource, out settings);
|
||||||
ProgramCopyright = appConfig.ProgramCopyright,
|
}
|
||||||
ProgramDataFolder = appConfig.ProgramDataFolder,
|
|
||||||
ProgramTitle = appConfig.ProgramTitle,
|
if (dataFormat != null)
|
||||||
ProgramVersion = appConfig.ProgramVersion,
|
{
|
||||||
RuntimeAddress = appConfig.RuntimeAddress,
|
return dataFormat.TryParse(data, out settings, adminPassword, settingsPassword);
|
||||||
RuntimeId = appConfig.RuntimeId,
|
}
|
||||||
RuntimeLogFile = appConfig.RuntimeLogFile,
|
}
|
||||||
SebUriScheme = appConfig.SebUriScheme,
|
|
||||||
SebUriSchemeSecure = appConfig.SebUriSchemeSecure,
|
logger.Warn($"No {(resourceLoader == null ? "resource loader" : "data format")} found for '{resource}'!");
|
||||||
ServiceAddress = appConfig.ServiceAddress
|
|
||||||
};
|
return LoadStatus.NotSupported;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Unexpected error while trying to load '{resource}'!", e);
|
||||||
|
|
||||||
|
return LoadStatus.UnexpectedError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LoadStatus HandleHtml(Uri resource, out Settings settings)
|
||||||
|
{
|
||||||
|
logger.Info($"Loaded data appears to be HTML, loading default settings and using '{resource}' as startup URL.");
|
||||||
|
|
||||||
|
settings = LoadDefaultSettings();
|
||||||
|
settings.Browser.StartUrl = resource.AbsoluteUri;
|
||||||
|
|
||||||
|
return LoadStatus.Success;
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] Decompress(byte[] bytes)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var buffer = new byte[4096];
|
||||||
|
|
||||||
|
using (var stream = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress))
|
||||||
|
using (var decompressed = new MemoryStream())
|
||||||
|
{
|
||||||
|
var bytesRead = 0;
|
||||||
|
|
||||||
|
do
|
||||||
|
{
|
||||||
|
bytesRead = stream.Read(buffer, 0, buffer.Length);
|
||||||
|
decompressed.Write(buffer, 0, bytesRead);
|
||||||
|
} while (bytesRead > 0);
|
||||||
|
|
||||||
|
return decompressed.ToArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (InvalidDataException)
|
||||||
|
{
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateAppConfig()
|
private void UpdateAppConfig()
|
||||||
|
|
37
SafeExamBrowser.Configuration/DataFormats/DefaultFormat.cs
Normal file
37
SafeExamBrowser.Configuration/DataFormats/DefaultFormat.cs
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
|
{
|
||||||
|
public class DefaultFormat : IDataFormat
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
public DefaultFormat(ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanParse(byte[] data)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null)
|
||||||
|
{
|
||||||
|
settings = new Settings();
|
||||||
|
settings.ServicePolicy = ServicePolicy.Optional;
|
||||||
|
|
||||||
|
return LoadStatus.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
SafeExamBrowser.Configuration/DataFormats/HtmlFormat.cs
Normal file
34
SafeExamBrowser.Configuration/DataFormats/HtmlFormat.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
|
{
|
||||||
|
public class HtmlFormat : IDataFormat
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
public HtmlFormat(ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanParse(byte[] data)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
Normal file
34
SafeExamBrowser.Configuration/DataFormats/XmlFormat.cs
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.DataFormats
|
||||||
|
{
|
||||||
|
public class XmlFormat : IDataFormat
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
public XmlFormat(ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanParse(byte[] data)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public LoadStatus TryParse(byte[] data, out Settings settings, string adminPassword = null, string settingsPassword = null)
|
||||||
|
{
|
||||||
|
throw new System.NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.Configuration;
|
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration
|
|
||||||
{
|
|
||||||
public class ResourceLoader : IResourceLoader
|
|
||||||
{
|
|
||||||
public bool IsHtmlResource(Uri resource)
|
|
||||||
{
|
|
||||||
// TODO: Implement resource loader
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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.IO;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.ResourceLoaders
|
||||||
|
{
|
||||||
|
public class FileResourceLoader : IResourceLoader
|
||||||
|
{
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
public FileResourceLoader(ILogger logger)
|
||||||
|
{
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanLoad(Uri resource)
|
||||||
|
{
|
||||||
|
if (resource.IsFile && File.Exists(resource.AbsolutePath))
|
||||||
|
{
|
||||||
|
logger.Debug($"Can load '{resource}' as it references an existing file.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"Can't load '{resource}' since it isn't a file URI or no file exists at the specified path.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Load(Uri resource)
|
||||||
|
{
|
||||||
|
logger.Debug($"Loading data from '{resource}'...");
|
||||||
|
|
||||||
|
return File.ReadAllBytes(resource.AbsolutePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,135 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
|
using SafeExamBrowser.Contracts.Logging;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.ResourceLoaders
|
||||||
|
{
|
||||||
|
public class NetworkResourceLoader : IResourceLoader
|
||||||
|
{
|
||||||
|
private AppConfig appConfig;
|
||||||
|
private ILogger logger;
|
||||||
|
|
||||||
|
private string[] SupportedSchemes => new[]
|
||||||
|
{
|
||||||
|
appConfig.SebUriScheme,
|
||||||
|
appConfig.SebUriSchemeSecure,
|
||||||
|
Uri.UriSchemeHttp,
|
||||||
|
Uri.UriSchemeHttps
|
||||||
|
};
|
||||||
|
|
||||||
|
public NetworkResourceLoader(AppConfig appConfig, ILogger logger)
|
||||||
|
{
|
||||||
|
this.appConfig = appConfig;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanLoad(Uri resource)
|
||||||
|
{
|
||||||
|
if (SupportedSchemes.Contains(resource.Scheme) && IsAvailable(resource))
|
||||||
|
{
|
||||||
|
logger.Debug($"Can load '{resource}' as it references an existing network resource.");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
logger.Debug($"Can't load '{resource}' since its URI scheme is not supported or the resource is unavailable.");
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] Load(Uri resource)
|
||||||
|
{
|
||||||
|
var uri = BuildUriFor(resource);
|
||||||
|
|
||||||
|
logger.Debug($"Downloading data from '{uri}'...");
|
||||||
|
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Get, uri);
|
||||||
|
var response = Execute(request);
|
||||||
|
|
||||||
|
logger.Debug($"Sent GET request for '{uri}', received response '{(int) response.StatusCode} - {response.ReasonPhrase}'.");
|
||||||
|
|
||||||
|
var data = Extract(response.Content);
|
||||||
|
|
||||||
|
logger.Debug($"Extracted {data.Length / 1000.0} KB data from response.");
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsAvailable(Uri resource)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var uri = BuildUriFor(resource);
|
||||||
|
var request = new HttpRequestMessage(HttpMethod.Head, uri);
|
||||||
|
var response = Execute(request);
|
||||||
|
|
||||||
|
logger.Debug($"Sent HEAD request for '{uri}', received response '{(int) response.StatusCode} - {response.ReasonPhrase}'.");
|
||||||
|
|
||||||
|
return response.IsSuccessStatusCode;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
logger.Error($"Failed to check availability of '{resource}'!", e);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Uri BuildUriFor(Uri resource)
|
||||||
|
{
|
||||||
|
var scheme = GetSchemeFor(resource);
|
||||||
|
var builder = new UriBuilder(resource) { Scheme = scheme };
|
||||||
|
|
||||||
|
return builder.Uri;
|
||||||
|
}
|
||||||
|
|
||||||
|
private string GetSchemeFor(Uri resource)
|
||||||
|
{
|
||||||
|
if (resource.Scheme == appConfig.SebUriScheme)
|
||||||
|
{
|
||||||
|
return Uri.UriSchemeHttp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resource.Scheme == appConfig.SebUriSchemeSecure)
|
||||||
|
{
|
||||||
|
return Uri.UriSchemeHttps;
|
||||||
|
}
|
||||||
|
|
||||||
|
return resource.Scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
private HttpResponseMessage Execute(HttpRequestMessage request)
|
||||||
|
{
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
using (var client = new HttpClient())
|
||||||
|
{
|
||||||
|
return await client.SendAsync(request);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return task.GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
private byte[] Extract(HttpContent content)
|
||||||
|
{
|
||||||
|
var task = Task.Run(async () =>
|
||||||
|
{
|
||||||
|
return await content.ReadAsByteArrayAsync();
|
||||||
|
});
|
||||||
|
|
||||||
|
return task.GetAwaiter().GetResult();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,13 +48,18 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
|
<Reference Include="System.Net.Http" />
|
||||||
<Reference Include="System.Windows.Forms" />
|
<Reference Include="System.Windows.Forms" />
|
||||||
<Reference Include="Microsoft.CSharp" />
|
<Reference Include="Microsoft.CSharp" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="ResourceLoader.cs" />
|
<Compile Include="DataFormats\DefaultFormat.cs" />
|
||||||
|
<Compile Include="DataFormats\HtmlFormat.cs" />
|
||||||
|
<Compile Include="DataFormats\XmlFormat.cs" />
|
||||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||||
<Compile Include="ConfigurationRepository.cs" />
|
<Compile Include="ConfigurationRepository.cs" />
|
||||||
|
<Compile Include="ResourceLoaders\FileResourceLoader.cs" />
|
||||||
|
<Compile Include="ResourceLoaders\NetworkResourceLoader.cs" />
|
||||||
<Compile Include="SessionConfiguration.cs" />
|
<Compile Include="SessionConfiguration.cs" />
|
||||||
<Compile Include="SystemInfo.cs" />
|
<Compile Include="SystemInfo.cs" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
|
@ -126,5 +126,13 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// The communication address of the service component.
|
/// The communication address of the service component.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string ServiceAddress { get; set; }
|
public string ServiceAddress { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a shallow clone.
|
||||||
|
/// </summary>
|
||||||
|
public AppConfig Clone()
|
||||||
|
{
|
||||||
|
return MemberwiseClone() as AppConfig;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,16 +25,25 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
ISessionConfiguration InitializeSessionConfiguration();
|
ISessionConfiguration InitializeSessionConfiguration();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Attempts to load settings from the specified resource, using the optional passwords. Returns a <see cref="LoadStatus"/>
|
|
||||||
/// indicating the result of the operation. As long as the result is not <see cref="LoadStatus.Success"/>, the declared
|
|
||||||
/// <paramref name="settings"/> will be <c>null</c>!
|
|
||||||
/// </summary>
|
|
||||||
LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads the default settings.
|
/// Loads the default settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Settings.Settings LoadDefaultSettings();
|
Settings.Settings LoadDefaultSettings();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the specified <see cref="IDataFormat"/> as option to parse configuration data.
|
||||||
|
/// </summary>
|
||||||
|
void Register(IDataFormat dataFormat);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers the specified <see cref="IResourceLoader"/> as option to load configuration data.
|
||||||
|
/// </summary>
|
||||||
|
void Register(IResourceLoader resourceLoader);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to load settings from the specified resource, using the optional passwords. As long as the result is not
|
||||||
|
/// <see cref="LoadStatus.Success"/>, the referenced settings may be <c>null</c> or in an undefinable state!
|
||||||
|
/// </summary>
|
||||||
|
LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
26
SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
Normal file
26
SafeExamBrowser.Contracts/Configuration/IDataFormat.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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.Contracts.Configuration
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the data format for a configuration file.
|
||||||
|
/// </summary>
|
||||||
|
public interface IDataFormat
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates whether the given data complies with the required format.
|
||||||
|
/// </summary>
|
||||||
|
bool CanParse(byte[] data);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to parse the given binary data.
|
||||||
|
/// </summary>
|
||||||
|
LoadStatus TryParse(byte[] data, out Settings.Settings settings, string adminPassword = null, string settingsPassword = null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,13 +11,18 @@ using System;
|
||||||
namespace SafeExamBrowser.Contracts.Configuration
|
namespace SafeExamBrowser.Contracts.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Loads configuration data from various sources (e.g. the internet) and provides related resource handling functionality.
|
/// Loads binary data from a particular resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IResourceLoader
|
public interface IResourceLoader
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates whether the given <see cref="Uri"/> identifies a HTML resource.
|
/// Indicates whether the resource loader is able to load data from the specified resource.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
bool IsHtmlResource(Uri resource);
|
bool CanLoad(Uri resource);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Loads the binary data from the specified resource.
|
||||||
|
/// </summary>
|
||||||
|
byte[] Load(Uri resource);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
namespace SafeExamBrowser.Contracts.Configuration
|
namespace SafeExamBrowser.Contracts.Configuration
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Defines all possible results of <see cref="IConfigurationRepository.LoadSettings(System.Uri)"/>.
|
/// Defines all possible results of an attempt to load a configuration file.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public enum LoadStatus
|
public enum LoadStatus
|
||||||
{
|
{
|
||||||
|
@ -23,14 +23,24 @@ namespace SafeExamBrowser.Contracts.Configuration
|
||||||
/// </summary>
|
/// </summary>
|
||||||
InvalidData,
|
InvalidData,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that a resource is not supported.
|
||||||
|
/// </summary>
|
||||||
|
NotSupported,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Indicates that a settings password is needed in order to load the settings.
|
/// Indicates that a settings password is needed in order to load the settings.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
SettingsPasswordNeeded,
|
SettingsPasswordNeeded,
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The <see cref="Settings.Settings"/> were loaded successfully.
|
/// The settings were loaded successfully.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
Success
|
Success,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An unexpected error occurred while trying to load the settings.
|
||||||
|
/// </summary>
|
||||||
|
UnexpectedError
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,18 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowBackwardNavigation { get; set; }
|
public bool AllowBackwardNavigation { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether the user should be allowed to download configuration files.
|
||||||
|
/// </summary>
|
||||||
|
public bool AllowConfigurationDownloads { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the user should be allowed to open the developer console of a browser window.
|
/// Determines whether the user should be allowed to open the developer console of a browser window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowDeveloperConsole { get; set; }
|
public bool AllowDeveloperConsole { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether the user should be allowed to download files.
|
/// Determines whether the user should be allowed to download files (excluding configuration files).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool AllowDownloads { get; set; }
|
public bool AllowDownloads { get; set; }
|
||||||
|
|
||||||
|
|
|
@ -22,6 +22,10 @@ namespace SafeExamBrowser.Contracts.I18n
|
||||||
MessageBox_ClientConfigurationQuestionTitle,
|
MessageBox_ClientConfigurationQuestionTitle,
|
||||||
MessageBox_ConfigurationDownloadError,
|
MessageBox_ConfigurationDownloadError,
|
||||||
MessageBox_ConfigurationDownloadErrorTitle,
|
MessageBox_ConfigurationDownloadErrorTitle,
|
||||||
|
MessageBox_InvalidConfigurationData,
|
||||||
|
MessageBox_InvalidConfigurationDataTitle,
|
||||||
|
MessageBox_NotSupportedConfigurationResource,
|
||||||
|
MessageBox_NotSupportedConfigurationResourceTitle,
|
||||||
MessageBox_Quit,
|
MessageBox_Quit,
|
||||||
MessageBox_QuitTitle,
|
MessageBox_QuitTitle,
|
||||||
MessageBox_QuitError,
|
MessageBox_QuitError,
|
||||||
|
@ -42,6 +46,8 @@ namespace SafeExamBrowser.Contracts.I18n
|
||||||
MessageBox_SingleInstanceTitle,
|
MessageBox_SingleInstanceTitle,
|
||||||
MessageBox_StartupError,
|
MessageBox_StartupError,
|
||||||
MessageBox_StartupErrorTitle,
|
MessageBox_StartupErrorTitle,
|
||||||
|
MessageBox_UnexpectedConfigurationError,
|
||||||
|
MessageBox_UnexpectedConfigurationErrorTitle,
|
||||||
Notification_AboutTooltip,
|
Notification_AboutTooltip,
|
||||||
Notification_LogTooltip,
|
Notification_LogTooltip,
|
||||||
OperationStatus_CloseRuntimeConnection,
|
OperationStatus_CloseRuntimeConnection,
|
||||||
|
|
|
@ -53,6 +53,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
|
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
|
||||||
|
<Compile Include="Configuration\IDataFormat.cs" />
|
||||||
<Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" />
|
<Compile Include="Core\Events\InstanceTerminatedEventHandler.cs" />
|
||||||
<Compile Include="Core\Events\NameChangedEventHandler.cs" />
|
<Compile Include="Core\Events\NameChangedEventHandler.cs" />
|
||||||
<Compile Include="Core\IApplicationController.cs" />
|
<Compile Include="Core\IApplicationController.cs" />
|
||||||
|
|
|
@ -24,6 +24,18 @@
|
||||||
<Entry key="MessageBox_ConfigurationDownloadErrorTitle">
|
<Entry key="MessageBox_ConfigurationDownloadErrorTitle">
|
||||||
Download Error
|
Download Error
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="MessageBox_InvalidConfigurationData">
|
||||||
|
The configuration resource '%%URI%%' contains invalid data!
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_InvalidConfigurationDataTitle">
|
||||||
|
Configuration Error
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_NotSupportedConfigurationResource">
|
||||||
|
The configuration resource '%%URI%%' is not supported!
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_NotSupportedConfigurationResourceTitle">
|
||||||
|
Configuration Error
|
||||||
|
</Entry>
|
||||||
<Entry key="MessageBox_Quit">
|
<Entry key="MessageBox_Quit">
|
||||||
Would you really like to quit the application?
|
Would you really like to quit the application?
|
||||||
</Entry>
|
</Entry>
|
||||||
|
@ -78,6 +90,12 @@
|
||||||
<Entry key="MessageBox_StartupErrorTitle">
|
<Entry key="MessageBox_StartupErrorTitle">
|
||||||
Startup Error
|
Startup Error
|
||||||
</Entry>
|
</Entry>
|
||||||
|
<Entry key="MessageBox_UnexpectedConfigurationError">
|
||||||
|
An unexpected error occurred while trying to load configuration resource '%%URI%%'! Please consult the application log for more information...
|
||||||
|
</Entry>
|
||||||
|
<Entry key="MessageBox_UnexpectedConfigurationErrorTitle">
|
||||||
|
Configuration Error
|
||||||
|
</Entry>
|
||||||
<Entry key="Notification_AboutTooltip">
|
<Entry key="Notification_AboutTooltip">
|
||||||
About Safe Exam Browser
|
About Safe Exam Browser
|
||||||
</Entry>
|
</Entry>
|
||||||
|
|
|
@ -26,7 +26,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
private Mock<ILogger> logger;
|
private Mock<ILogger> logger;
|
||||||
private Mock<IConfigurationRepository> repository;
|
private Mock<IConfigurationRepository> repository;
|
||||||
private Mock<IResourceLoader> resourceLoader;
|
|
||||||
private Mock<ISessionConfiguration> session;
|
private Mock<ISessionConfiguration> session;
|
||||||
private SessionContext sessionContext;
|
private SessionContext sessionContext;
|
||||||
private Settings settings;
|
private Settings settings;
|
||||||
|
@ -39,7 +38,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
appConfig = new AppConfig();
|
appConfig = new AppConfig();
|
||||||
logger = new Mock<ILogger>();
|
logger = new Mock<ILogger>();
|
||||||
repository = new Mock<IConfigurationRepository>();
|
repository = new Mock<IConfigurationRepository>();
|
||||||
resourceLoader = new Mock<IResourceLoader>();
|
|
||||||
session = new Mock<ISessionConfiguration>();
|
session = new Mock<ISessionConfiguration>();
|
||||||
sessionContext = new SessionContext();
|
sessionContext = new SessionContext();
|
||||||
settings = new Settings();
|
settings = new Settings();
|
||||||
|
@ -64,7 +62,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
var resource = new Uri(url);
|
var resource = new Uri(url);
|
||||||
|
@ -82,7 +80,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
|
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
|
||||||
|
@ -99,7 +97,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
|
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
|
||||||
|
@ -110,10 +108,19 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustFallbackToDefaultsAsLastPrio()
|
public void MustFallbackToDefaultsAsLastPrio()
|
||||||
{
|
{
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
var actualSettings = default(Settings);
|
||||||
|
var defaultSettings = new Settings();
|
||||||
|
|
||||||
|
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
|
||||||
|
session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
|
||||||
|
|
||||||
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
|
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
|
||||||
|
session.VerifySet(s => s.Settings = defaultSettings);
|
||||||
|
|
||||||
|
Assert.AreSame(defaultSettings, actualSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@ -122,7 +129,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
|
appConfig.ProgramDataFolder = Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), nameof(Operations));
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is ConfigurationCompletedEventArgs c)
|
if (args is ConfigurationCompletedEventArgs c)
|
||||||
|
@ -141,7 +148,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
{
|
{
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is ConfigurationCompletedEventArgs c)
|
if (args is ConfigurationCompletedEventArgs c)
|
||||||
|
@ -161,7 +168,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
settings.ConfigurationMode = ConfigurationMode.Exam;
|
settings.ConfigurationMode = ConfigurationMode.Exam;
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is ConfigurationCompletedEventArgs c)
|
if (args is ConfigurationCompletedEventArgs c)
|
||||||
|
@ -176,15 +183,25 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustNotFailWithoutCommandLineArgs()
|
public void MustNotFailWithoutCommandLineArgs()
|
||||||
{
|
{
|
||||||
repository.Setup(r => r.LoadDefaultSettings());
|
var actualSettings = default(Settings);
|
||||||
|
var defaultSettings = new Settings();
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
repository.Setup(r => r.LoadDefaultSettings()).Returns(defaultSettings);
|
||||||
|
session.SetupSet<Settings>(s => s.Settings = It.IsAny<Settings>()).Callback(s => actualSettings = s);
|
||||||
|
|
||||||
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
|
||||||
|
|
||||||
|
Assert.AreSame(defaultSettings, actualSettings);
|
||||||
|
|
||||||
|
sut = new ConfigurationOperation(new string[] { }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
|
|
||||||
repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
|
repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
|
||||||
|
|
||||||
|
Assert.AreSame(defaultSettings, actualSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
|
@ -192,7 +209,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
{
|
{
|
||||||
var uri = @"an/invalid\uri.'*%yolo/()你好";
|
var uri = @"an/invalid\uri.'*%yolo/()你好";
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", uri }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.Perform();
|
sut.Perform();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -203,7 +220,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -224,7 +241,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -247,7 +264,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, password, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -272,7 +289,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, password)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, password)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -295,7 +312,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.AdminPasswordNeeded);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -316,7 +333,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -341,7 +358,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, settingsPassword)).Returns(LoadStatus.AdminPasswordNeeded);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, settingsPassword)).Returns(LoadStatus.AdminPasswordNeeded);
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, sessionContext);
|
||||||
sut.ActionRequired += args =>
|
sut.ActionRequired += args =>
|
||||||
{
|
{
|
||||||
if (args is PasswordRequiredEventArgs p)
|
if (args is PasswordRequiredEventArgs p)
|
||||||
|
@ -358,37 +375,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword), Times.Once);
|
repository.Verify(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, adminPassword, settingsPassword), Times.Once);
|
||||||
}
|
}
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void MustHandleInvalidData()
|
|
||||||
{
|
|
||||||
var url = @"http://www.safeexambrowser.org/whatever.seb";
|
|
||||||
|
|
||||||
resourceLoader.Setup(r => r.IsHtmlResource(It.IsAny<Uri>())).Returns(false);
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.InvalidData);
|
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
|
||||||
|
|
||||||
var result = sut.Perform();
|
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Failed, result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
|
||||||
public void MustHandleHtmlAsInvalidData()
|
|
||||||
{
|
|
||||||
var url = "http://www.blubb.org/some/resource.html";
|
|
||||||
|
|
||||||
resourceLoader.Setup(r => r.IsHtmlResource(It.IsAny<Uri>())).Returns(true);
|
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.InvalidData);
|
|
||||||
|
|
||||||
sut = new ConfigurationOperation(new[] { "blubb.exe", url }, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
|
||||||
|
|
||||||
var result = sut.Perform();
|
|
||||||
|
|
||||||
Assert.AreEqual(OperationResult.Success, result);
|
|
||||||
Assert.AreEqual(url, settings.Browser.StartUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
[TestMethod]
|
[TestMethod]
|
||||||
public void MustReconfigureSuccessfullyWithCorrectUri()
|
public void MustReconfigureSuccessfullyWithCorrectUri()
|
||||||
{
|
{
|
||||||
|
@ -398,7 +384,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
sessionContext.ReconfigurationFilePath = resource.AbsolutePath;
|
sessionContext.ReconfigurationFilePath = resource.AbsolutePath;
|
||||||
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.Is<Uri>(u => u.Equals(resource)), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
|
|
||||||
var result = sut.Repeat();
|
var result = sut.Repeat();
|
||||||
|
|
||||||
|
@ -415,7 +401,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||||
sessionContext.ReconfigurationFilePath = null;
|
sessionContext.ReconfigurationFilePath = null;
|
||||||
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
repository.Setup(r => r.TryLoadSettings(It.IsAny<Uri>(), out settings, null, null)).Returns(LoadStatus.Success);
|
||||||
|
|
||||||
sut = new ConfigurationOperation(null, repository.Object, logger.Object, resourceLoader.Object, sessionContext);
|
sut = new ConfigurationOperation(null, repository.Object, logger.Object, sessionContext);
|
||||||
|
|
||||||
var result = sut.Repeat();
|
var result = sut.Repeat();
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,10 @@
|
||||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
<bindingRedirect oldVersion="0.0.0.0-4.1.1.0" newVersion="4.1.1.0" />
|
||||||
</dependentAssembly>
|
</dependentAssembly>
|
||||||
|
<dependentAssembly>
|
||||||
|
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||||
|
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||||
|
</dependentAssembly>
|
||||||
</assemblyBinding>
|
</assemblyBinding>
|
||||||
</runtime>
|
</runtime>
|
||||||
</configuration>
|
</configuration>
|
|
@ -13,6 +13,8 @@ using System.Reflection;
|
||||||
using SafeExamBrowser.Communication.Hosts;
|
using SafeExamBrowser.Communication.Hosts;
|
||||||
using SafeExamBrowser.Communication.Proxies;
|
using SafeExamBrowser.Communication.Proxies;
|
||||||
using SafeExamBrowser.Configuration;
|
using SafeExamBrowser.Configuration;
|
||||||
|
using SafeExamBrowser.Configuration.DataFormats;
|
||||||
|
using SafeExamBrowser.Configuration.ResourceLoaders;
|
||||||
using SafeExamBrowser.Contracts.Configuration;
|
using SafeExamBrowser.Contracts.Configuration;
|
||||||
using SafeExamBrowser.Contracts.Core;
|
using SafeExamBrowser.Contracts.Core;
|
||||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||||
|
@ -32,6 +34,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
internal class CompositionRoot
|
internal class CompositionRoot
|
||||||
{
|
{
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
|
private IConfigurationRepository configuration;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private ISystemInfo systemInfo;
|
private ISystemInfo systemInfo;
|
||||||
private IText text;
|
private IText text;
|
||||||
|
@ -45,13 +48,12 @@ namespace SafeExamBrowser.Runtime
|
||||||
const int FIFTEEN_SECONDS = 15000;
|
const int FIFTEEN_SECONDS = 15000;
|
||||||
|
|
||||||
var args = Environment.GetCommandLineArgs();
|
var args = Environment.GetCommandLineArgs();
|
||||||
var configuration = BuildConfigurationRepository();
|
|
||||||
var nativeMethods = new NativeMethods();
|
var nativeMethods = new NativeMethods();
|
||||||
|
|
||||||
logger = new Logger();
|
logger = new Logger();
|
||||||
appConfig = configuration.InitializeAppConfig();
|
|
||||||
systemInfo = new SystemInfo();
|
systemInfo = new SystemInfo();
|
||||||
|
|
||||||
|
InitializeConfiguration();
|
||||||
InitializeLogging();
|
InitializeLogging();
|
||||||
InitializeText();
|
InitializeText();
|
||||||
|
|
||||||
|
@ -60,7 +62,6 @@ namespace SafeExamBrowser.Runtime
|
||||||
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods);
|
var explorerShell = new ExplorerShell(new ModuleLogger(logger, nameof(ExplorerShell)), nativeMethods);
|
||||||
var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory)));
|
var processFactory = new ProcessFactory(new ModuleLogger(logger, nameof(ProcessFactory)));
|
||||||
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
|
var proxyFactory = new ProxyFactory(new ProxyObjectFactory(), logger);
|
||||||
var resourceLoader = new ResourceLoader();
|
|
||||||
var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), new ModuleLogger(logger, nameof(RuntimeHost)), FIVE_SECONDS);
|
var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), new ModuleLogger(logger, nameof(RuntimeHost)), FIVE_SECONDS);
|
||||||
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ServiceProxy)));
|
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), new ModuleLogger(logger, nameof(ServiceProxy)));
|
||||||
var sessionContext = new SessionContext();
|
var sessionContext = new SessionContext();
|
||||||
|
@ -73,7 +74,7 @@ namespace SafeExamBrowser.Runtime
|
||||||
bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger));
|
bootstrapOperations.Enqueue(new CommunicationHostOperation(runtimeHost, logger));
|
||||||
|
|
||||||
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext));
|
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost, sessionContext));
|
||||||
sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, logger, resourceLoader, sessionContext));
|
sessionOperations.Enqueue(new ConfigurationOperation(args, configuration, logger, sessionContext));
|
||||||
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS));
|
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, FIFTEEN_SECONDS));
|
||||||
sessionOperations.Enqueue(new KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));
|
sessionOperations.Enqueue(new KioskModeTerminationOperation(desktopFactory, explorerShell, logger, processFactory, sessionContext));
|
||||||
sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, sessionContext));
|
sessionOperations.Enqueue(new ServiceOperation(logger, serviceProxy, sessionContext));
|
||||||
|
@ -105,15 +106,22 @@ namespace SafeExamBrowser.Runtime
|
||||||
logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
|
||||||
}
|
}
|
||||||
|
|
||||||
private IConfigurationRepository BuildConfigurationRepository()
|
private void InitializeConfiguration()
|
||||||
{
|
{
|
||||||
var executable = Assembly.GetExecutingAssembly();
|
var executable = Assembly.GetExecutingAssembly();
|
||||||
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 repository = new ConfigurationRepository(executable.Location, programCopyright, programTitle, programVersion);
|
var moduleLogger = new ModuleLogger(logger, nameof(ConfigurationRepository));
|
||||||
|
|
||||||
return repository;
|
configuration = new ConfigurationRepository(moduleLogger, executable.Location, programCopyright, programTitle, programVersion);
|
||||||
|
appConfig = configuration.InitializeAppConfig();
|
||||||
|
|
||||||
|
configuration.Register(new DefaultFormat(new ModuleLogger(logger, nameof(DefaultFormat))));
|
||||||
|
configuration.Register(new HtmlFormat(new ModuleLogger(logger, nameof(HtmlFormat))));
|
||||||
|
configuration.Register(new XmlFormat(new ModuleLogger(logger, nameof(XmlFormat))));
|
||||||
|
configuration.Register(new FileResourceLoader(new ModuleLogger(logger, nameof(FileResourceLoader))));
|
||||||
|
configuration.Register(new NetworkResourceLoader(appConfig, new ModuleLogger(logger, nameof(NetworkResourceLoader))));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitializeLogging()
|
private void InitializeLogging()
|
||||||
|
|
|
@ -24,7 +24,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
private string[] commandLineArgs;
|
private string[] commandLineArgs;
|
||||||
private IConfigurationRepository configuration;
|
private IConfigurationRepository configuration;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IResourceLoader resourceLoader;
|
|
||||||
|
|
||||||
public override event ActionRequiredEventHandler ActionRequired;
|
public override event ActionRequiredEventHandler ActionRequired;
|
||||||
public override event StatusChangedEventHandler StatusChanged;
|
public override event StatusChangedEventHandler StatusChanged;
|
||||||
|
@ -33,13 +32,11 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
string[] commandLineArgs,
|
string[] commandLineArgs,
|
||||||
IConfigurationRepository configuration,
|
IConfigurationRepository configuration,
|
||||||
ILogger logger,
|
ILogger logger,
|
||||||
IResourceLoader resourceLoader,
|
|
||||||
SessionContext sessionContext) : base(sessionContext)
|
SessionContext sessionContext) : base(sessionContext)
|
||||||
{
|
{
|
||||||
this.commandLineArgs = commandLineArgs;
|
this.commandLineArgs = commandLineArgs;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.configuration = configuration;
|
this.configuration = configuration;
|
||||||
this.resourceLoader = resourceLoader;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override OperationResult Perform()
|
public override OperationResult Perform()
|
||||||
|
@ -51,7 +48,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
if (isValidUri)
|
if (isValidUri)
|
||||||
{
|
{
|
||||||
logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'...");
|
logger.Info($"Attempting to load settings from '{uri}'...");
|
||||||
|
|
||||||
var result = LoadSettings(uri);
|
var result = LoadSettings(uri);
|
||||||
|
|
||||||
|
@ -62,7 +59,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
}
|
}
|
||||||
|
|
||||||
logger.Info("No valid settings resource specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
|
logger.Info("No valid settings resource specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
|
||||||
configuration.LoadDefaultSettings();
|
Context.Next.Settings = configuration.LoadDefaultSettings();
|
||||||
|
|
||||||
return OperationResult.Success;
|
return OperationResult.Success;
|
||||||
}
|
}
|
||||||
|
@ -76,7 +73,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
|
|
||||||
if (isValidUri)
|
if (isValidUri)
|
||||||
{
|
{
|
||||||
logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'...");
|
logger.Info($"Attempting to load settings from '{uri}'...");
|
||||||
|
|
||||||
var result = LoadSettings(uri);
|
var result = LoadSettings(uri);
|
||||||
|
|
||||||
|
@ -126,11 +123,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == LoadStatus.InvalidData)
|
|
||||||
{
|
|
||||||
HandleInvalidData(ref status, uri);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (status == LoadStatus.Success)
|
if (status == LoadStatus.Success)
|
||||||
{
|
{
|
||||||
Context.Next.Settings = settings;
|
Context.Next.Settings = settings;
|
||||||
|
@ -138,6 +130,18 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
return OperationResult.Success;
|
return OperationResult.Success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case LoadStatus.InvalidData:
|
||||||
|
ActionRequired?.Invoke(new InvalidDataMessageArgs(uri.ToString()));
|
||||||
|
break;
|
||||||
|
case LoadStatus.NotSupported:
|
||||||
|
ActionRequired?.Invoke(new NotSupportedMessageArgs(uri.ToString()));
|
||||||
|
break;
|
||||||
|
case LoadStatus.UnexpectedError:
|
||||||
|
ActionRequired?.Invoke(new UnexpectedErrorMessageArgs(uri.ToString()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
return OperationResult.Failed;
|
return OperationResult.Failed;
|
||||||
}
|
}
|
||||||
|
@ -152,22 +156,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
return args;
|
return args;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void HandleInvalidData(ref LoadStatus status, Uri uri)
|
|
||||||
{
|
|
||||||
if (resourceLoader.IsHtmlResource(uri))
|
|
||||||
{
|
|
||||||
configuration.LoadDefaultSettings();
|
|
||||||
Context.Next.Settings.Browser.StartUrl = uri.AbsoluteUri;
|
|
||||||
logger.Info($"The specified URI '{uri.AbsoluteUri}' appears to point to a HTML resource, setting it as startup URL.");
|
|
||||||
|
|
||||||
status = LoadStatus.Success;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
logger.Error($"The specified settings resource '{uri.AbsoluteUri}' is invalid!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool TryInitializeSettingsUri(out Uri uri)
|
private bool TryInitializeSettingsUri(out Uri uri)
|
||||||
{
|
{
|
||||||
var path = string.Empty;
|
var path = string.Empty;
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.I18n;
|
||||||
|
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||||
|
{
|
||||||
|
internal class InvalidDataMessageArgs : MessageEventArgs
|
||||||
|
{
|
||||||
|
internal InvalidDataMessageArgs(string uri)
|
||||||
|
{
|
||||||
|
Icon = MessageBoxIcon.Error;
|
||||||
|
Message = TextKey.MessageBox_InvalidConfigurationData;
|
||||||
|
MessagePlaceholders["%%URI%%"] = uri;
|
||||||
|
Title = TextKey.MessageBox_InvalidConfigurationDataTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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.Collections.Generic;
|
||||||
|
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||||
|
using SafeExamBrowser.Contracts.I18n;
|
||||||
|
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||||
|
{
|
||||||
|
internal class MessageEventArgs : ActionRequiredEventArgs
|
||||||
|
{
|
||||||
|
internal MessageBoxIcon Icon { get; set; }
|
||||||
|
internal TextKey Message { get; set; }
|
||||||
|
internal TextKey Title { get; set; }
|
||||||
|
internal Dictionary<string, string> MessagePlaceholders { get; private set; }
|
||||||
|
internal Dictionary<string, string> TitlePlaceholders { get; private set; }
|
||||||
|
|
||||||
|
public MessageEventArgs()
|
||||||
|
{
|
||||||
|
MessagePlaceholders = new Dictionary<string, string>();
|
||||||
|
TitlePlaceholders = new Dictionary<string, string>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.I18n;
|
||||||
|
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||||
|
{
|
||||||
|
internal class NotSupportedMessageArgs : MessageEventArgs
|
||||||
|
{
|
||||||
|
internal NotSupportedMessageArgs(string uri)
|
||||||
|
{
|
||||||
|
Icon = MessageBoxIcon.Error;
|
||||||
|
Message = TextKey.MessageBox_NotSupportedConfigurationResource;
|
||||||
|
MessagePlaceholders["%%URI%%"] = uri;
|
||||||
|
Title = TextKey.MessageBox_NotSupportedConfigurationResourceTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.I18n;
|
||||||
|
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||||
|
{
|
||||||
|
internal class UnexpectedErrorMessageArgs : MessageEventArgs
|
||||||
|
{
|
||||||
|
internal UnexpectedErrorMessageArgs(string uri)
|
||||||
|
{
|
||||||
|
Icon = MessageBoxIcon.Error;
|
||||||
|
Message = TextKey.MessageBox_UnexpectedConfigurationError;
|
||||||
|
MessagePlaceholders["%%URI%%"] = uri;
|
||||||
|
Title = TextKey.MessageBox_UnexpectedConfigurationErrorTitle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -35,10 +35,6 @@ namespace SafeExamBrowser.Runtime.Operations
|
||||||
set { Context.OriginalDesktop = value; }
|
set { Context.OriginalDesktop = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// TODO: This mechanism exposes the internal state of the operation! Find better solution which will keep the
|
|
||||||
/// state internal but still allow unit testing of both kiosk mode operations independently!
|
|
||||||
/// </summary>
|
|
||||||
protected KioskMode? ActiveMode
|
protected KioskMode? ActiveMode
|
||||||
{
|
{
|
||||||
get { return Context.ActiveMode; }
|
get { return Context.ActiveMode; }
|
||||||
|
|
|
@ -363,6 +363,9 @@ namespace SafeExamBrowser.Runtime
|
||||||
case ConfigurationCompletedEventArgs a:
|
case ConfigurationCompletedEventArgs a:
|
||||||
AskIfConfigurationSufficient(a);
|
AskIfConfigurationSufficient(a);
|
||||||
break;
|
break;
|
||||||
|
case MessageEventArgs m:
|
||||||
|
ShowMessageBox(m);
|
||||||
|
break;
|
||||||
case PasswordRequiredEventArgs p:
|
case PasswordRequiredEventArgs p:
|
||||||
AskForPassword(p);
|
AskForPassword(p);
|
||||||
break;
|
break;
|
||||||
|
@ -393,6 +396,24 @@ namespace SafeExamBrowser.Runtime
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void ShowMessageBox(MessageEventArgs args)
|
||||||
|
{
|
||||||
|
var message = text.Get(args.Message);
|
||||||
|
var title = text.Get(args.Title);
|
||||||
|
|
||||||
|
foreach (var placeholder in args.MessagePlaceholders)
|
||||||
|
{
|
||||||
|
message = message.Replace(placeholder.Key, placeholder.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var placeholder in args.TitlePlaceholders)
|
||||||
|
{
|
||||||
|
title = title.Replace(placeholder.Key, placeholder.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
messageBox.Show(message, title, MessageBoxAction.Confirm, args.Icon, runtimeWindow);
|
||||||
|
}
|
||||||
|
|
||||||
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)
|
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)
|
||||||
{
|
{
|
||||||
var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator;
|
var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator;
|
||||||
|
|
|
@ -90,7 +90,11 @@
|
||||||
<Compile Include="Operations\ClientTerminationOperation.cs" />
|
<Compile Include="Operations\ClientTerminationOperation.cs" />
|
||||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||||
<Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" />
|
<Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" />
|
||||||
|
<Compile Include="Operations\Events\InvalidDataMessageArgs.cs" />
|
||||||
|
<Compile Include="Operations\Events\MessageEventArgs.cs" />
|
||||||
|
<Compile Include="Operations\Events\NotSupportedMessageArgs.cs" />
|
||||||
<Compile Include="Operations\Events\PasswordRequiredEventArgs.cs" />
|
<Compile Include="Operations\Events\PasswordRequiredEventArgs.cs" />
|
||||||
|
<Compile Include="Operations\Events\UnexpectedErrorMessageArgs.cs" />
|
||||||
<Compile Include="Operations\KioskModeOperation.cs" />
|
<Compile Include="Operations\KioskModeOperation.cs" />
|
||||||
<Compile Include="Operations\KioskModeTerminationOperation.cs" />
|
<Compile Include="Operations\KioskModeTerminationOperation.cs" />
|
||||||
<Compile Include="Operations\ServiceOperation.cs" />
|
<Compile Include="Operations\ServiceOperation.cs" />
|
||||||
|
|
|
@ -64,6 +64,7 @@ namespace SafeExamBrowser.WindowsApi
|
||||||
logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}.");
|
logger.Info($"Restored window '{window.Title}' with handle = {window.Handle}.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
minimizedWindows.Clear();
|
||||||
logger.Info("Minimized windows successfully restored.");
|
logger.Info("Minimized windows successfully restored.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue