SEBWIN-220: Scaffolding for new (re-)configuration procedure.

This commit is contained in:
dbuechel 2018-06-21 07:56:25 +02:00
parent 45dd741e4c
commit 50dcb7502a
54 changed files with 710 additions and 256 deletions

14
.editorconfig Normal file
View file

@ -0,0 +1,14 @@
# Defines coding style deviations from the Microsoft Minimum Recommended Rules ruleset, which is active per default in Visual Studio 2017
# For more info, see https://editorconfig.org/ and https://docs.microsoft.com/en-us/visualstudio/ide/create-portable-custom-editor-options
root = true
[*]
end_of_line = crlf
[*.cs]
dotnet_style_object_initializer = false:none
indent_style = tab
[*.xml]
indent_style = space

View file

@ -8,12 +8,10 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.ComponentModel;
using System.Linq; using System.Linq;
using CefSharp; using CefSharp;
using SafeExamBrowser.Browser.Handlers; using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
@ -24,30 +22,30 @@ using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.Browser
namespace SafeExamBrowser.Browser namespace SafeExamBrowser.Browser
{ {
public class BrowserApplicationController : IApplicationController public class BrowserApplicationController : IBrowserApplicationController
{ {
private IApplicationButton button; private IApplicationButton button;
private IList<IApplicationInstance> instances = new List<IApplicationInstance>(); private IList<IApplicationInstance> instances;
private BrowserSettings settings;
private ILogger logger; private ILogger logger;
private IMessageBox messageBox; private IMessageBox messageBox;
private IRuntimeProxy runtime;
private RuntimeInfo runtimeInfo; private RuntimeInfo runtimeInfo;
private IUserInterfaceFactory uiFactory; private BrowserSettings settings;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory;
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public BrowserApplicationController( public BrowserApplicationController(
BrowserSettings settings, BrowserSettings settings,
RuntimeInfo runtimeInfo, RuntimeInfo runtimeInfo,
ILogger logger, ILogger logger,
IMessageBox messageBox, IMessageBox messageBox,
IRuntimeProxy runtime,
IText text, IText text,
IUserInterfaceFactory uiFactory) IUserInterfaceFactory uiFactory)
{ {
this.instances = new List<IApplicationInstance>();
this.logger = logger; this.logger = logger;
this.messageBox = messageBox; this.messageBox = messageBox;
this.runtime = runtime;
this.runtimeInfo = runtimeInfo; this.runtimeInfo = runtimeInfo;
this.settings = settings; this.settings = settings;
this.text = text; this.text = text;
@ -84,15 +82,14 @@ namespace SafeExamBrowser.Browser
private void CreateNewInstance() private void CreateNewInstance()
{ {
var instance = new BrowserApplicationInstance(settings, text, uiFactory, instances.Count == 0); var instance = new BrowserApplicationInstance(settings, runtimeInfo, text, uiFactory, instances.Count == 0);
instance.Initialize(); instance.Initialize();
instance.ConfigurationDetected += Instance_ConfigurationDetected; instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
instance.Terminated += Instance_Terminated; instance.Terminated += Instance_Terminated;
button.RegisterInstance(instance); button.RegisterInstance(instance);
instances.Add(instance); instances.Add(instance);
instance.Window.Show(); instance.Window.Show();
} }
@ -124,36 +121,6 @@ namespace SafeExamBrowser.Browser
} }
} }
private void Instance_ConfigurationDetected(string url, CancelEventArgs args)
{
var result = messageBox.Show(TextKey.MessageBox_ReconfigurationQuestion, TextKey.MessageBox_ReconfigurationQuestionTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
var reconfigure = result == MessageBoxResult.Yes;
var allowed = false;
logger.Info($"Detected configuration request for '{url}'. The user chose to {(reconfigure ? "start" : "abort")} the reconfiguration.");
if (reconfigure)
{
try
{
allowed = runtime.RequestReconfiguration(url);
logger.Info($"The runtime {(allowed ? "accepted" : "denied")} the reconfiguration request.");
if (!allowed)
{
messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle);
}
}
catch (Exception e)
{
logger.Error("Failed to communicate the reconfiguration request to the runtime!", e);
messageBox.Show(TextKey.MessageBox_ReconfigurationError, TextKey.MessageBox_ReconfigurationErrorTitle, icon: MessageBoxIcon.Error);
}
}
args.Cancel = !allowed;
}
private void Instance_Terminated(Guid id) private void Instance_Terminated(Guid id)
{ {
instances.Remove(instances.FirstOrDefault(i => i.Id == id)); instances.Remove(instances.FirstOrDefault(i => i.Id == id));

View file

@ -8,6 +8,7 @@
using System; using System;
using SafeExamBrowser.Browser.Handlers; using SafeExamBrowser.Browser.Handlers;
using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n; using SafeExamBrowser.Contracts.I18n;
@ -22,6 +23,7 @@ namespace SafeExamBrowser.Browser
private IBrowserControl control; private IBrowserControl control;
private IBrowserWindow window; private IBrowserWindow window;
private bool isMainInstance; private bool isMainInstance;
private RuntimeInfo runtimeInfo;
private BrowserSettings settings; private BrowserSettings settings;
private IText text; private IText text;
private IUserInterfaceFactory uiFactory; private IUserInterfaceFactory uiFactory;
@ -30,13 +32,19 @@ namespace SafeExamBrowser.Browser
public string Name { get; private set; } public string Name { get; private set; }
public IWindow Window { get { return window; } } public IWindow Window { get { return window; } }
internal event ConfigurationDetectedEventHandler ConfigurationDetected; public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event TerminatedEventHandler Terminated;
public event NameChangedEventHandler NameChanged; public event NameChangedEventHandler NameChanged;
public event TerminatedEventHandler Terminated;
public BrowserApplicationInstance(BrowserSettings settings, IText text, IUserInterfaceFactory uiFactory, bool isMainInstance) public BrowserApplicationInstance(
BrowserSettings settings,
RuntimeInfo runtimeInfo,
IText text,
IUserInterfaceFactory uiFactory,
bool isMainInstance)
{ {
this.isMainInstance = isMainInstance; this.isMainInstance = isMainInstance;
this.runtimeInfo = runtimeInfo;
this.settings = settings; this.settings = settings;
this.text = text; this.text = text;
this.uiFactory = uiFactory; this.uiFactory = uiFactory;
@ -44,13 +52,17 @@ namespace SafeExamBrowser.Browser
internal void Initialize() internal void Initialize()
{ {
var downloadHandler = new DownloadHandler(settings, runtimeInfo);
Id = Guid.NewGuid(); Id = Guid.NewGuid();
downloadHandler.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
control = new BrowserControl(settings, text); control = new BrowserControl(settings, text);
control.AddressChanged += Control_AddressChanged; control.AddressChanged += Control_AddressChanged;
(control as BrowserControl).ConfigurationDetected += (url, args) => ConfigurationDetected?.Invoke(url, args);
control.LoadingStateChanged += Control_LoadingStateChanged; control.LoadingStateChanged += Control_LoadingStateChanged;
control.TitleChanged += Control_TitleChanged; control.TitleChanged += Control_TitleChanged;
(control as BrowserControl).DownloadHandler = downloadHandler;
(control as BrowserControl).Initialize();
window = uiFactory.CreateBrowserWindow(control, settings); window = uiFactory.CreateBrowserWindow(control, settings);
window.IsMainWindow = isMainInstance; window.IsMainWindow = isMainInstance;

View file

@ -24,8 +24,6 @@ namespace SafeExamBrowser.Browser
private LoadingStateChangedEventHandler loadingStateChanged; private LoadingStateChangedEventHandler loadingStateChanged;
private TitleChangedEventHandler titleChanged; private TitleChangedEventHandler titleChanged;
internal event ConfigurationDetectedEventHandler ConfigurationDetected;
event AddressChangedEventHandler IBrowserControl.AddressChanged event AddressChangedEventHandler IBrowserControl.AddressChanged
{ {
add { addressChanged += value; } add { addressChanged += value; }
@ -48,8 +46,17 @@ namespace SafeExamBrowser.Browser
{ {
this.settings = settings; this.settings = settings;
this.text = text; this.text = text;
}
Initialize(); public void Initialize()
{
AddressChanged += (o, args) => addressChanged?.Invoke(args.Address);
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
KeyboardHandler = new KeyboardHandler(settings);
MenuHandler = new ContextMenuHandler(settings, text);
RequestHandler = new RequestHandler();
} }
public void NavigateBackwards() public void NavigateBackwards()
@ -74,19 +81,5 @@ namespace SafeExamBrowser.Browser
{ {
GetBrowser().Reload(); GetBrowser().Reload();
} }
private void Initialize()
{
var requestHandler = new RequestHandler();
AddressChanged += (o, args) => addressChanged?.Invoke(args.Address);
LoadingStateChanged += (o, args) => loadingStateChanged?.Invoke(args.IsLoading);
TitleChanged += (o, args) => titleChanged?.Invoke(args.Title);
requestHandler.ConfigurationDetected += (url, args) => ConfigurationDetected?.Invoke(url, args);
KeyboardHandler = new KeyboardHandler(settings);
MenuHandler = new ContextMenuHandler(settings, text);
RequestHandler = requestHandler;
}
} }
} }

View file

@ -0,0 +1,88 @@
/*
* 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.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;
using CefSharp;
using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Configuration;
using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings;
namespace SafeExamBrowser.Browser.Handlers
{
/// <remarks>
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_IDownloadHandler.htm.
/// </remarks>
internal class DownloadHandler : IDownloadHandler
{
private BrowserSettings settings;
private RuntimeInfo runtimeInfo;
private ConcurrentDictionary<int, DownloadFinishedCallback> callbacks;
public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public DownloadHandler(BrowserSettings settings, RuntimeInfo runtimeInfo)
{
this.callbacks = new ConcurrentDictionary<int, DownloadFinishedCallback>();
this.settings = settings;
this.runtimeInfo = runtimeInfo;
}
public void OnBeforeDownload(IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback)
{
var uri = new Uri(downloadItem.Url);
var extension = Path.GetExtension(uri.AbsolutePath);
var isConfigFile = String.Equals(extension, runtimeInfo.ConfigurationFileExtension, StringComparison.InvariantCultureIgnoreCase);
if (isConfigFile)
{
Task.Run(() => RequestConfigurationFileDownload(downloadItem, callback));
}
else if (!isConfigFile && settings.AllowDownloads)
{
using (callback)
{
callback.Continue(null, true);
}
}
}
public void OnDownloadUpdated(IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback)
{
if (downloadItem.IsComplete || downloadItem.IsCancelled)
{
if (callbacks.TryRemove(downloadItem.Id, out DownloadFinishedCallback finished) && finished != null)
{
Task.Run(() => finished.Invoke(downloadItem.IsComplete, downloadItem.FullPath));
}
}
}
private void RequestConfigurationFileDownload(DownloadItem downloadItem, IBeforeDownloadCallback callback)
{
var args = new DownloadEventArgs();
ConfigurationDownloadRequested?.Invoke(downloadItem.SuggestedFileName, args);
if (args.AllowDownload)
{
if (args.Callback != null)
{
callbacks[downloadItem.Id] = args.Callback;
}
using (callback)
{
callback.Continue(args.DownloadPath, false);
}
}
}
}
}

View file

@ -7,46 +7,31 @@
*/ */
using System; using System;
using System.ComponentModel;
using System.IO;
using System.Threading.Tasks;
using CefSharp; using CefSharp;
using CefSharp.Handler; using CefSharp.Handler;
namespace SafeExamBrowser.Browser.Handlers namespace SafeExamBrowser.Browser.Handlers
{ {
internal delegate void ConfigurationDetectedEventHandler(string url, CancelEventArgs args);
/// <remarks> /// <remarks>
/// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_Handler_DefaultRequestHandler.htm. /// See https://cefsharp.github.io/api/63.0.0/html/T_CefSharp_Handler_DefaultRequestHandler.htm.
/// </remarks> /// </remarks>
internal class RequestHandler : DefaultRequestHandler internal class RequestHandler : DefaultRequestHandler
{ {
internal event ConfigurationDetectedEventHandler ConfigurationDetected;
public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback) public override CefReturnValue OnBeforeResourceLoad(IWebBrowser browserControl, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
{ {
Task.Run(() => var uri = new Uri(request.Url);
// TODO: Move to globals -> SafeExamBrowserUriScheme, SafeExamBrowserSecureUriScheme
if (uri.Scheme == "seb")
{ {
var allow = true; request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttp }.ToString();
var uri = new Uri(request.Url); }
else if (uri.Scheme == "sebs")
{
request.Url = new UriBuilder(uri) { Scheme = Uri.UriSchemeHttps }.ToString();
}
if (uri.Scheme == "seb" || uri.Scheme == "sebs" || Path.HasExtension("seb")) return base.OnBeforeResourceLoad(browserControl, browser, frame, request, callback);
{
var args = new CancelEventArgs();
ConfigurationDetected?.Invoke(request.Url, args);
allow = !args.Cancel;
}
using (callback)
{
callback.Continue(allow);
}
});
return CefReturnValue.ContinueAsync;
} }
} }
} }

View file

@ -51,7 +51,10 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
@ -67,6 +70,7 @@
<SubType>Component</SubType> <SubType>Component</SubType>
</Compile> </Compile>
<Compile Include="BrowserIconResource.cs" /> <Compile Include="BrowserIconResource.cs" />
<Compile Include="Handlers\DownloadHandler.cs" />
<Compile Include="Handlers\KeyboardHandler.cs" /> <Compile Include="Handlers\KeyboardHandler.cs" />
<Compile Include="Handlers\RequestHandler.cs" /> <Compile Include="Handlers\RequestHandler.cs" />
<Compile Include="Handlers\SchemeHandlerFactory.cs" /> <Compile Include="Handlers\SchemeHandlerFactory.cs" />

View file

@ -53,7 +53,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">

View file

@ -7,8 +7,10 @@
*/ */
using System; using System;
using System.IO;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.OperationModel; using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies; using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
@ -38,6 +40,7 @@ namespace SafeExamBrowser.Client.Behaviour
private IWindowMonitor windowMonitor; private IWindowMonitor windowMonitor;
private RuntimeInfo runtimeInfo; private RuntimeInfo runtimeInfo;
public IBrowserApplicationController Browser { private get; set; }
public IClientHost ClientHost { private get; set; } public IClientHost ClientHost { private get; set; }
public Guid SessionId { private get; set; } public Guid SessionId { private get; set; }
public Settings Settings { private get; set; } public Settings Settings { private get; set; }
@ -145,6 +148,7 @@ namespace SafeExamBrowser.Client.Behaviour
private void RegisterEvents() private void RegisterEvents()
{ {
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
ClientHost.Shutdown += ClientHost_Shutdown; ClientHost.Shutdown += ClientHost_Shutdown;
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted += ProcessMonitor_ExplorerStarted;
@ -155,6 +159,7 @@ namespace SafeExamBrowser.Client.Behaviour
private void DeregisterEvents() private void DeregisterEvents()
{ {
Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested;
ClientHost.Shutdown -= ClientHost_Shutdown; ClientHost.Shutdown -= ClientHost_Shutdown;
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged; displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted; processMonitor.ExplorerStarted -= ProcessMonitor_ExplorerStarted;
@ -183,6 +188,53 @@ namespace SafeExamBrowser.Client.Behaviour
logger.Info("Desktop successfully restored."); logger.Info("Desktop successfully restored.");
} }
private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
{
if (Settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
{
logger.Info($"Detected download request for configuration file '{fileName}'.");
var result = messageBox.Show(TextKey.MessageBox_ReconfigurationQuestion, TextKey.MessageBox_ReconfigurationQuestionTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
var reconfigure = result == MessageBoxResult.Yes;
logger.Info($"The user chose to {(reconfigure ? "start" : "abort")} the reconfiguration.");
if (reconfigure)
{
args.AllowDownload = true;
args.Callback = Browser_ConfigurationDownloadFinished;
args.DownloadPath = Path.Combine(runtimeInfo.DownloadDirectory, fileName);
}
}
else
{
logger.Info($"Denied download request for configuration file '{fileName}' due to '{Settings.ConfigurationMode}' mode.");
messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle);
}
}
private void Browser_ConfigurationDownloadFinished(bool success, string filePath = null)
{
if (success)
{
try
{
runtime.RequestReconfiguration(filePath);
logger.Info($"Sent reconfiguration request for '{filePath}' to the runtime.");
}
catch (Exception e)
{
logger.Error($"Failed to communicate reconfiguration request for '{filePath}'!", e);
messageBox.Show(TextKey.MessageBox_ReconfigurationError, TextKey.MessageBox_ReconfigurationErrorTitle, icon: MessageBoxIcon.Error);
}
}
else
{
logger.Error($"Failed to download configuration file '{filePath}'!");
messageBox.Show(TextKey.MessageBox_ConfigurationDownloadError, TextKey.MessageBox_ConfigurationDownloadErrorTitle, icon: MessageBoxIcon.Error);
}
}
private void ClientHost_Shutdown() private void ClientHost_Shutdown()
{ {
taskbar.Close(); taskbar.Close();

View file

@ -7,8 +7,8 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Data; using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication.Hosts; using SafeExamBrowser.Core.Communication.Hosts;

View file

@ -17,6 +17,7 @@ using SafeExamBrowser.Client.Notifications;
using SafeExamBrowser.Configuration; using SafeExamBrowser.Configuration;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.OperationModel; using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies; using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
@ -47,6 +48,7 @@ namespace SafeExamBrowser.Client
private string runtimeHostUri; private string runtimeHostUri;
private Guid startupToken; private Guid startupToken;
private IBrowserApplicationController browserController;
private ClientConfiguration configuration; private ClientConfiguration configuration;
private IClientHost clientHost; private IClientHost clientHost;
private ILogger logger; private ILogger logger;
@ -88,7 +90,6 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, startupToken)); operations.Enqueue(new RuntimeConnectionOperation(logger, runtimeProxy, startupToken));
operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy)); operations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeProxy));
operations.Enqueue(new DelayedInitializationOperation(BuildCommunicationHostOperation)); operations.Enqueue(new DelayedInitializationOperation(BuildCommunicationHostOperation));
operations.Enqueue(new DelegateOperation(UpdateClientControllerDependencies));
// TODO // TODO
//operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation)); //operations.Enqueue(new DelayedInitializationOperation(BuildKeyboardInterceptorOperation));
//operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor)); //operations.Enqueue(new WindowMonitorOperation(logger, windowMonitor));
@ -98,6 +99,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation)); operations.Enqueue(new DelayedInitializationOperation(BuildBrowserOperation));
operations.Enqueue(new ClipboardOperation(logger, nativeMethods)); operations.Enqueue(new ClipboardOperation(logger, nativeMethods));
//operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation)); //operations.Enqueue(new DelayedInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new DelegateOperation(UpdateClientControllerDependencies));
var sequence = new OperationSequence(logger, operations); var sequence = new OperationSequence(logger, operations);
@ -150,10 +152,12 @@ namespace SafeExamBrowser.Client
private IOperation BuildBrowserOperation() private IOperation BuildBrowserOperation()
{ {
var moduleLogger = new ModuleLogger(logger, typeof(BrowserApplicationController)); var moduleLogger = new ModuleLogger(logger, typeof(BrowserApplicationController));
var browserController = new BrowserApplicationController(configuration.Settings.Browser, configuration.RuntimeInfo, moduleLogger, messageBox, runtimeProxy, text, uiFactory); var browserController = new BrowserApplicationController(configuration.Settings.Browser, configuration.RuntimeInfo, moduleLogger, messageBox, text, uiFactory);
var browserInfo = new BrowserApplicationInfo(); var browserInfo = new BrowserApplicationInfo();
var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory); var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory);
this.browserController = browserController;
return operation; return operation;
} }
@ -200,6 +204,7 @@ namespace SafeExamBrowser.Client
private void UpdateClientControllerDependencies() private void UpdateClientControllerDependencies()
{ {
ClientController.Browser = browserController;
ClientController.ClientHost = clientHost; ClientController.ClientHost = clientHost;
ClientController.RuntimeInfo = configuration.RuntimeInfo; ClientController.RuntimeInfo = configuration.RuntimeInfo;
ClientController.SessionId = configuration.SessionId; ClientController.SessionId = configuration.SessionId;

View file

@ -44,8 +44,8 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
@ -54,7 +54,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View file

@ -23,7 +23,7 @@ namespace SafeExamBrowser.Configuration
public ISessionData CurrentSession { get; private set; } public ISessionData CurrentSession { get; private set; }
public Settings CurrentSettings { get; private set; } public Settings CurrentSettings { get; private set; }
public string ReconfigurationUrl { get; set; } public string ReconfigurationFilePath { get; set; }
public RuntimeInfo RuntimeInfo public RuntimeInfo RuntimeInfo
{ {
@ -66,35 +66,34 @@ namespace SafeExamBrowser.Configuration
} }
} }
public Settings LoadSettings(Uri path) public LoadStatus LoadSettings(Uri resource, string settingsPassword = null, string adminPassword = null)
{ {
// TODO: Implement loading mechanism // TODO: Implement loading mechanism
return LoadDefaultSettings(); LoadDefaultSettings();
return LoadStatus.Success;
} }
public Settings LoadDefaultSettings() public void LoadDefaultSettings()
{ {
var settings = new Settings() // TODO: Implement default settings
{
// TODO: Implement default settings
ServicePolicy = ServicePolicy.Optional
};
settings.Browser.StartUrl = "https://www.safeexambrowser.org/testing"; CurrentSettings = new Settings();
settings.Browser.AllowAddressBar = true;
settings.Browser.AllowBackwardNavigation = true;
settings.Browser.AllowDeveloperConsole = true;
settings.Browser.AllowForwardNavigation = true;
settings.Browser.AllowReloading = true;
settings.Taskbar.AllowApplicationLog = true; CurrentSettings.ServicePolicy = ServicePolicy.Optional;
settings.Taskbar.AllowKeyboardLayout = true;
settings.Taskbar.AllowWirelessNetwork = true;
CurrentSettings = settings; CurrentSettings.Browser.StartUrl = "https://www.safeexambrowser.org/testing";
CurrentSettings.Browser.AllowAddressBar = true;
CurrentSettings.Browser.AllowBackwardNavigation = true;
CurrentSettings.Browser.AllowDeveloperConsole = true;
CurrentSettings.Browser.AllowForwardNavigation = true;
CurrentSettings.Browser.AllowReloading = true;
CurrentSettings.Browser.AllowDownloads = true;
return settings; CurrentSettings.Taskbar.AllowApplicationLog = true;
CurrentSettings.Taskbar.AllowKeyboardLayout = true;
CurrentSettings.Taskbar.AllowWirelessNetwork = true;
} }
private void InitializeRuntimeInfo() private void InitializeRuntimeInfo()
@ -105,26 +104,26 @@ namespace SafeExamBrowser.Configuration
var logFolder = Path.Combine(appDataFolder, "Logs"); var logFolder = Path.Combine(appDataFolder, "Logs");
var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s");
runtimeInfo = new RuntimeInfo runtimeInfo = new RuntimeInfo();
{ runtimeInfo.ApplicationStartTime = startTime;
ApplicationStartTime = startTime, runtimeInfo.AppDataFolder = appDataFolder;
AppDataFolder = appDataFolder, runtimeInfo.BrowserCachePath = Path.Combine(appDataFolder, "Cache");
BrowserCachePath = Path.Combine(appDataFolder, "Cache"), runtimeInfo.BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt");
BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"), runtimeInfo.ClientId = Guid.NewGuid();
ClientId = Guid.NewGuid(), runtimeInfo.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}";
ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}", runtimeInfo.ClientExecutablePath = Path.Combine(Path.GetDirectoryName(executable.Location), $"{nameof(SafeExamBrowser)}.Client.exe");
ClientExecutablePath = Path.Combine(Path.GetDirectoryName(executable.Location), $"{nameof(SafeExamBrowser)}.Client.exe"), runtimeInfo.ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt");
ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"), runtimeInfo.ConfigurationFileExtension = ".seb";
DefaultSettingsFileName = "SebClientSettings.seb", runtimeInfo.DefaultSettingsFileName = "SebClientSettings.seb";
ProgramCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright, runtimeInfo.DownloadDirectory = Path.Combine(appDataFolder, "Downloads");
ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser)), runtimeInfo.ProgramCopyright = executable.GetCustomAttribute<AssemblyCopyrightAttribute>().Copyright;
ProgramTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title, runtimeInfo.ProgramDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), nameof(SafeExamBrowser));
ProgramVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion, runtimeInfo.ProgramTitle = executable.GetCustomAttribute<AssemblyTitleAttribute>().Title;
RuntimeId = Guid.NewGuid(), runtimeInfo.ProgramVersion = executable.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion;
RuntimeAddress = $"{BASE_ADDRESS}/runtime/{Guid.NewGuid()}", runtimeInfo.RuntimeId = Guid.NewGuid();
RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"), runtimeInfo.RuntimeAddress = $"{BASE_ADDRESS}/runtime/{Guid.NewGuid()}";
ServiceAddress = $"{BASE_ADDRESS}/service" runtimeInfo.RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt");
}; runtimeInfo.ServiceAddress = $"{BASE_ADDRESS}/service";
} }
private void UpdateRuntimeInfo() private void UpdateRuntimeInfo()

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Browser;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Configuration.Settings;
@ -18,6 +19,11 @@ namespace SafeExamBrowser.Contracts.Behaviour
/// </summary> /// </summary>
public interface IClientController public interface IClientController
{ {
/// <summary>
/// The controller for the browser application.
/// </summary>
IBrowserApplicationController Browser { set; }
/// <summary> /// <summary>
/// The client host used for communication handling. /// The client host used for communication handling.
/// </summary> /// </summary>

View file

@ -0,0 +1,31 @@
/*
* 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.Browser
{
/// <summary>
/// TODO
/// </summary>
public class DownloadEventArgs
{
/// <summary>
/// Determines whether the specified download is allowed.
/// </summary>
public bool AllowDownload { get; set; }
/// <summary>
/// Callback executed once a download has been finished.
/// </summary>
public DownloadFinishedCallback Callback { get; set; }
/// <summary>
/// The full path under which the specified file should be saved.
/// </summary>
public string DownloadPath { get; set; }
}
}

View file

@ -0,0 +1,12 @@
/*
* 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.Browser
{
public delegate void DownloadFinishedCallback(bool success, string filePath = null);
}

View file

@ -0,0 +1,12 @@
/*
* 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.Browser
{
public delegate void DownloadRequestedEventHandler(string fileName, DownloadEventArgs args);
}

View file

@ -0,0 +1,23 @@
/*
* 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.Behaviour;
namespace SafeExamBrowser.Contracts.Browser
{
/// <summary>
/// Controls the lifetime and functionality of the browser application.
/// </summary>
public interface IBrowserApplicationController : IApplicationController
{
/// <summary>
/// Event fired when the browser application detects a download request for an application configuration file.
/// </summary>
event DownloadRequestedEventHandler ConfigurationDownloadRequested;
}
}

View file

@ -17,13 +17,13 @@ namespace SafeExamBrowser.Contracts.Communication.Data
public class ReconfigurationMessage : Message public class ReconfigurationMessage : Message
{ {
/// <summary> /// <summary>
/// The locator of the new configuration to be used. /// The full path of the configuration file to be used.
/// </summary> /// </summary>
public string ConfigurationUrl { get; private set; } public string ConfigurationPath { get; private set; }
public ReconfigurationMessage(string url) public ReconfigurationMessage(string path)
{ {
ConfigurationUrl = url; ConfigurationPath = path;
} }
} }
} }

View file

@ -0,0 +1,17 @@
/*
* 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.Communication.Events
{
/// <summary>
/// Base class which must be used for all event parameters <c>T</c> of <see cref="CommunicationEventHandler{T}"/>.
/// </summary>
public abstract class CommunicationEventArgs
{
}
}

View file

@ -0,0 +1,41 @@
/*
* 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.Threading.Tasks;
namespace SafeExamBrowser.Contracts.Communication.Events
{
/// <summary>
/// The default handler for communication events of an interlocutor.
/// </summary>
public delegate void CommunicationEventHandler();
/// <summary>
/// The handler with parameter for communication events of an interlocutor.
/// </summary>
public delegate void CommunicationEventHandler<T>(T args) where T : CommunicationEventArgs;
public static class CommunicationEventHandlerExtensions
{
/// <summary>
/// Executes the event handler asynchronously, i.e. on a separate thread.
/// </summary>
public static async Task InvokeAsync(this CommunicationEventHandler handler)
{
await Task.Run(() => handler?.Invoke());
}
/// <summary>
/// Executes the event handler asynchronously, i.e. on a separate thread.
/// </summary>
public static async Task InvokeAsync<T>(this CommunicationEventHandler<T> handler, T args) where T : CommunicationEventArgs
{
await Task.Run(() => handler?.Invoke(args));
}
}
}

View file

@ -0,0 +1,21 @@
/*
* 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.Communication.Events
{
/// <summary>
/// The event arguments used for the reconfiguration event fired by the <see cref="Hosts.IRuntimeHost"/>.
/// </summary>
public class ReconfigurationEventArgs : CommunicationEventArgs
{
/// <summary>
/// The full path to the configuration file to be used for reconfiguration.
/// </summary>
public string ConfigurationPath { get; set; }
}
}

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Communication.Events;
namespace SafeExamBrowser.Contracts.Communication.Hosts namespace SafeExamBrowser.Contracts.Communication.Hosts
{ {

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Communication.Events;
namespace SafeExamBrowser.Contracts.Communication.Hosts namespace SafeExamBrowser.Contracts.Communication.Hosts
{ {
@ -31,9 +32,9 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
event CommunicationEventHandler ClientReady; event CommunicationEventHandler ClientReady;
/// <summary> /// <summary>
/// Event fired when the client detected a reconfiguration request. /// Event fired when the client requested a reconfiguration of the application.
/// </summary> /// </summary>
event CommunicationEventHandler ReconfigurationRequested; event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
/// <summary> /// <summary>
/// Event fired when the client requests to shut down the application. /// Event fired when the client requests to shut down the application.

View file

@ -8,8 +8,6 @@
namespace SafeExamBrowser.Contracts.Communication namespace SafeExamBrowser.Contracts.Communication
{ {
public delegate void CommunicationEventHandler();
/// <summary> /// <summary>
/// Defines the common functionality for all communication hosts. A communication host can be hosted by an application component to /// Defines the common functionality for all communication hosts. A communication host can be hosted by an application component to
/// allow for inter-process communication with other components (e.g. runtime -> client communication). /// allow for inter-process communication with other components (e.g. runtime -> client communication).

View file

@ -7,6 +7,7 @@
*/ */
using System; using System;
using SafeExamBrowser.Contracts.Communication.Events;
namespace SafeExamBrowser.Contracts.Communication namespace SafeExamBrowser.Contracts.Communication
{ {

View file

@ -34,10 +34,9 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
void RequestShutdown(); void RequestShutdown();
/// <summary> /// <summary>
/// Requests the runtime to reconfigure the application with the configuration from the given location. Returns <c>true</c> if /// Requests the runtime to reconfigure the application with the specified configuration.
/// the runtime accepted the request, otherwise <c>false</c>.
/// </summary> /// </summary>
/// <exception cref="System.ServiceModel.*">If the communication failed.</exception> /// <exception cref="System.ServiceModel.*">If the communication failed.</exception>
bool RequestReconfiguration(string url); void RequestReconfiguration(string filePath);
} }
} }

View file

@ -28,9 +28,9 @@ namespace SafeExamBrowser.Contracts.Configuration
Settings.Settings CurrentSettings { get; } Settings.Settings CurrentSettings { get; }
/// <summary> /// <summary>
/// The locator of the configuration to be used when reconfiguring the application. /// The path of the settings file to be used when reconfiguring the application.
/// </summary> /// </summary>
string ReconfigurationUrl { get; set; } string ReconfigurationFilePath { get; set; }
/// <summary> /// <summary>
/// The runtime information for the currently running application instance. /// The runtime information for the currently running application instance.
@ -48,14 +48,14 @@ namespace SafeExamBrowser.Contracts.Configuration
void InitializeSessionConfiguration(); void InitializeSessionConfiguration();
/// <summary> /// <summary>
/// Attempts to load settings from the specified path. /// Attempts to load settings from the specified resource, using the optional passwords. Returns a <see cref="LoadStatus"/>
/// indicating the result of the operation.
/// </summary> /// </summary>
/// <exception cref="ArgumentException">Thrown if the given path cannot be resolved to a settings file.</exception> LoadStatus LoadSettings(Uri resource, string settingsPassword = null, string adminPassword = null);
Settings.Settings LoadSettings(Uri path);
/// <summary> /// <summary>
/// Loads the default settings. /// Loads the default settings.
/// </summary> /// </summary>
Settings.Settings LoadDefaultSettings(); void LoadDefaultSettings();
} }
} }

View file

@ -0,0 +1,36 @@
/*
* 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 all possible results of <see cref="IConfigurationRepository.LoadSettings(System.Uri)"/>.
/// </summary>
public enum LoadStatus
{
/// <summary>
/// Indicates that an admin password is needed in order to load the settings.
/// </summary>
AdminPasswordNeeded = 1,
/// <summary>
/// Indicates that a resource does not comply with the required data format.
/// </summary>
InvalidData,
/// <summary>
/// Indicates that a settings password is needed in order to load the settings.
/// </summary>
SettingsPasswordNeeded,
/// <summary>
/// The <see cref="Settings.Settings"/> were loaded successfully.
/// </summary>
Success
}
}

View file

@ -12,6 +12,7 @@ namespace SafeExamBrowser.Contracts.Configuration
{ {
/// <summary> /// <summary>
/// Defines the fundamental, global configuration information for all application components. /// Defines the fundamental, global configuration information for all application components.
/// TODO: Rename to Globals or GlobalConfiguration or alike!
/// </summary> /// </summary>
[Serializable] [Serializable]
public class RuntimeInfo public class RuntimeInfo
@ -56,11 +57,21 @@ namespace SafeExamBrowser.Contracts.Configuration
/// </summary> /// </summary>
public string ClientLogFile { get; set; } public string ClientLogFile { get; set; }
/// <summary>
/// The file extension of configuration files for the application (including the period).
/// </summary>
public string ConfigurationFileExtension { get; set; }
/// <summary> /// <summary>
/// The default file name for application settings. /// The default file name for application settings.
/// </summary> /// </summary>
public string DefaultSettingsFileName { get; set; } public string DefaultSettingsFileName { get; set; }
/// <summary>
/// The default directory for file downloads.
/// </summary>
public string DownloadDirectory { get; set; }
/// <summary> /// <summary>
/// The copyright information for the application (i.e. the executing assembly). /// The copyright information for the application (i.e. the executing assembly).
/// </summary> /// </summary>

View file

@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Configuration.Settings
/// </summary> /// </summary>
public bool AllowDeveloperConsole { get; set; } public bool AllowDeveloperConsole { get; set; }
/// <summary>
/// Determines whether the user should be allowed to download files.
/// </summary>
public bool AllowDownloads { get; set; }
/// <summary> /// <summary>
/// Determines whether the user should be allowed to navigate forwards in a browser window. /// Determines whether the user should be allowed to navigate forwards in a browser window.
/// </summary> /// </summary>

View file

@ -20,6 +20,8 @@ namespace SafeExamBrowser.Contracts.I18n
MessageBox_ApplicationErrorTitle, MessageBox_ApplicationErrorTitle,
MessageBox_ClientConfigurationQuestion, MessageBox_ClientConfigurationQuestion,
MessageBox_ClientConfigurationQuestionTitle, MessageBox_ClientConfigurationQuestionTitle,
MessageBox_ConfigurationDownloadError,
MessageBox_ConfigurationDownloadErrorTitle,
MessageBox_Quit, MessageBox_Quit,
MessageBox_QuitTitle, MessageBox_QuitTitle,
MessageBox_QuitError, MessageBox_QuitError,

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />
@ -57,6 +56,13 @@
<Compile Include="Behaviour\IRuntimeController.cs" /> <Compile Include="Behaviour\IRuntimeController.cs" />
<Compile Include="Behaviour\OperationModel\IOperationSequence.cs" /> <Compile Include="Behaviour\OperationModel\IOperationSequence.cs" />
<Compile Include="Behaviour\OperationModel\OperationResult.cs" /> <Compile Include="Behaviour\OperationModel\OperationResult.cs" />
<Compile Include="Browser\DownloadEventArgs.cs" />
<Compile Include="Browser\DownloadFinishedCallback.cs" />
<Compile Include="Browser\DownloadRequestedEventHandler.cs" />
<Compile Include="Browser\IBrowserApplicationController.cs" />
<Compile Include="Communication\Events\CommunicationEventArgs.cs" />
<Compile Include="Communication\Events\CommunicationEventHandler.cs" />
<Compile Include="Communication\Events\ReconfigurationEventArgs.cs" />
<Compile Include="Communication\Hosts\IClientHost.cs" /> <Compile Include="Communication\Hosts\IClientHost.cs" />
<Compile Include="Communication\Hosts\IHostObject.cs" /> <Compile Include="Communication\Hosts\IHostObject.cs" />
<Compile Include="Communication\Hosts\IHostObjectFactory.cs" /> <Compile Include="Communication\Hosts\IHostObjectFactory.cs" />
@ -84,6 +90,7 @@
<Compile Include="Communication\Data\SimpleResponsePurport.cs" /> <Compile Include="Communication\Data\SimpleResponsePurport.cs" />
<Compile Include="Communication\Data\SimpleResponse.cs" /> <Compile Include="Communication\Data\SimpleResponse.cs" />
<Compile Include="Configuration\ClientConfiguration.cs" /> <Compile Include="Configuration\ClientConfiguration.cs" />
<Compile Include="Configuration\LoadStatus.cs" />
<Compile Include="Configuration\RuntimeInfo.cs" /> <Compile Include="Configuration\RuntimeInfo.cs" />
<Compile Include="Configuration\ISessionData.cs" /> <Compile Include="Configuration\ISessionData.cs" />
<Compile Include="Configuration\Settings\ConfigurationMode.cs" /> <Compile Include="Configuration\Settings\ConfigurationMode.cs" />

View file

@ -96,47 +96,56 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Proxies
[TestMethod] [TestMethod]
public void MustCorrectlyRequestReconfiguration() public void MustCorrectlyRequestReconfiguration()
{ {
var url = "sebs://some/url.seb"; //var url = "sebs://some/url.seb";
var response = new ReconfigurationResponse //var response = new ReconfigurationResponse
{ //{
Accepted = true // Accepted = true
}; //};
proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns(response); //proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns(response);
var accepted = sut.RequestReconfiguration(url); //var accepted = sut.RequestReconfiguration(url);
proxy.Verify(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url)), Times.Once); //proxy.Verify(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url)), Times.Once);
Assert.IsTrue(accepted); //Assert.IsTrue(accepted);
// TODO
Assert.Fail();
} }
[TestMethod] [TestMethod]
public void MustCorrectlyHandleDeniedReconfigurationRequest() public void MustCorrectlyHandleDeniedReconfigurationRequest()
{ {
var url = "sebs://some/url.seb"; //var url = "sebs://some/url.seb";
var response = new ReconfigurationResponse //var response = new ReconfigurationResponse
{ //{
Accepted = false // Accepted = false
}; //};
proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns(response); //proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns(response);
var accepted = sut.RequestReconfiguration(url); //var accepted = sut.RequestReconfiguration(url);
Assert.IsFalse(accepted); //Assert.IsFalse(accepted);
// TODO
Assert.Fail();
} }
[TestMethod] [TestMethod]
public void MustNotFailIfIncorrectResponseToReconfigurationRequest() public void MustNotFailIfIncorrectResponseToReconfigurationRequest()
{ {
var url = "sebs://some/url.seb"; //var url = "sebs://some/url.seb";
proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns<Response>(null); //proxy.Setup(p => p.Send(It.Is<ReconfigurationMessage>(m => m.ConfigurationUrl == url))).Returns<Response>(null);
var accepted = sut.RequestReconfiguration(url); //var accepted = sut.RequestReconfiguration(url);
Assert.IsFalse(accepted); //Assert.IsFalse(accepted);
// TODO
Assert.Fail();
} }
[TestMethod] [TestMethod]

View file

@ -53,7 +53,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">

View file

@ -11,6 +11,7 @@ using System.ServiceModel;
using System.Timers; using System.Timers;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Data; using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Proxies; using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;

View file

@ -45,16 +45,14 @@ namespace SafeExamBrowser.Core.Communication.Proxies
} }
} }
public bool RequestReconfiguration(string url) public void RequestReconfiguration(string filePath)
{ {
var response = Send(new ReconfigurationMessage(url)); var response = Send(new ReconfigurationMessage(filePath));
if (response is ReconfigurationResponse reconfiguration) if (!IsAcknowledged(response))
{ {
return reconfiguration.Accepted; throw new CommunicationException($"Runtime did not acknowledge reconfiguration request! Response: {ToString(response)}.");
} }
return false;
} }
public void RequestShutdown() public void RequestShutdown()

View file

@ -18,6 +18,12 @@
<Entry key="MessageBox_ClientConfigurationQuestionTitle"> <Entry key="MessageBox_ClientConfigurationQuestionTitle">
Configuration Successful Configuration Successful
</Entry> </Entry>
<Entry key="MessageBox_ConfigurationDownloadError">
Failed to download the new application configuration. Please try again or contact technical support.
</Entry>
<Entry key="MessageBox_ConfigurationDownloadErrorTitle">
Download 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>

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -45,8 +45,9 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info.AppDataFolder = @"C:\Not\Really\AppData"; info.AppDataFolder = @"C:\Not\Really\AppData";
info.DefaultSettingsFileName = "SettingsDummy.txt"; info.DefaultSettingsFileName = "SettingsDummy.txt";
info.ProgramDataFolder = @"C:\Not\Really\ProgramData"; info.ProgramDataFolder = @"C:\Not\Really\ProgramData";
repository.Setup(r => r.LoadSettings(It.IsAny<Uri>())).Returns(settings); // TODO
repository.Setup(r => r.LoadDefaultSettings()).Returns(settings); //repository.Setup(r => r.LoadSettings(It.IsAny<Uri>())).Returns(settings);
//repository.Setup(r => r.LoadDefaultSettings()).Returns(settings);
} }
[TestMethod] [TestMethod]
@ -88,7 +89,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(path)))), Times.Once); Assert.Fail();
//repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(path)))), Times.Once);
} }
[TestMethod] [TestMethod]
@ -103,7 +105,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); Assert.Fail();
//repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
} }
[TestMethod] [TestMethod]
@ -117,7 +120,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
sut.Perform(); sut.Perform();
repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once); Assert.Fail();
//repository.Verify(r => r.LoadSettings(It.Is<Uri>(u => u.Equals(new Uri(Path.Combine(location, "SettingsDummy.txt"))))), Times.Once);
} }
[TestMethod] [TestMethod]

View file

@ -53,7 +53,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL"> <Reference Include="Castle.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">

View file

@ -75,6 +75,9 @@ namespace SafeExamBrowser.Runtime
instances.RuntimeController.Terminate(); instances.RuntimeController.Terminate();
instances.LogShutdownInformation(); instances.LogShutdownInformation();
// TODO: Which UI operation is being cancelled without the timeout? Same problem with client? -> Debug!
Thread.Sleep(20);
base.Shutdown(); base.Shutdown();
} }

View file

@ -8,7 +8,7 @@
using System.Threading; using System.Threading;
using SafeExamBrowser.Contracts.Behaviour.OperationModel; using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies; using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;

View file

@ -50,35 +50,58 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
logger.Info("Initializing application configuration..."); logger.Info("Initializing application configuration...");
ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeConfiguration); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeConfiguration);
var isValidUri = TryGetSettingsUri(out Uri uri); var isValidUri = TryInitializeSettingsUri(out Uri uri);
if (isValidUri) if (isValidUri)
{ {
logger.Info($"Loading configuration from '{uri.AbsolutePath}'..."); logger.Info($"Loading settings from '{uri.AbsolutePath}'...");
var abort = LoadSettings(uri); var result = LoadSettings(uri);
if (abort) if (result == OperationResult.Success && repository.CurrentSettings.ConfigurationMode == ConfigurationMode.ConfigureClient)
{ {
return OperationResult.Aborted; var abort = IsConfigurationSufficient();
logger.Info($"The user chose to {(abort ? "abort" : "continue")} after successful client configuration.");
if (abort)
{
return OperationResult.Aborted;
}
} }
LogOperationResult(result);
return result;
} }
else
{ logger.Info("No valid settings resource specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
logger.Info("No valid settings file specified nor found in PROGRAMDATA or APPDATA - loading default settings..."); repository.LoadDefaultSettings();
repository.LoadDefaultSettings();
}
return OperationResult.Success; return OperationResult.Success;
} }
public OperationResult Repeat() public OperationResult Repeat()
{ {
// TODO: How will the new settings be retrieved? Uri passed to the repository? If yes, how does the Uri get here?! logger.Info("Initializing new application configuration...");
// -> IDEA: Use configuration repository as container? ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_InitializeConfiguration);
// -> IDEA: Introduce IRepeatParams or alike?
return OperationResult.Success; var isValidUri = TryValidateSettingsUri(repository.ReconfigurationFilePath, out Uri uri);
if (isValidUri)
{
logger.Info($"Loading settings from '{uri.AbsolutePath}'...");
var result = LoadSettings(uri);
LogOperationResult(result);
return result;
}
logger.Warn($"The resource specified for reconfiguration does not exist or is not a file!");
return OperationResult.Failed;
} }
public void Revert() public void Revert()
@ -86,7 +109,79 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
// Nothing to do here... // Nothing to do here...
} }
private bool TryGetSettingsUri(out Uri uri) private OperationResult LoadSettings(Uri uri)
{
var adminPassword = default(string);
var settingsPassword = default(string);
var status = default(LoadStatus);
for (int adminAttempts = 0, settingsAttempts = 0; adminAttempts < 5 && settingsAttempts < 5;)
{
status = repository.LoadSettings(uri, settingsPassword, adminPassword);
if (status == LoadStatus.InvalidData || status == LoadStatus.Success)
{
break;
}
else if (status == LoadStatus.AdminPasswordNeeded || status == LoadStatus.SettingsPasswordNeeded)
{
var isAdmin = status == LoadStatus.AdminPasswordNeeded;
var success = isAdmin ? TryGetAdminPassword(out adminPassword) : TryGetSettingsPassword(out settingsPassword);
if (success)
{
adminAttempts += isAdmin ? 1 : 0;
settingsAttempts += isAdmin ? 0 : 1;
}
else
{
return OperationResult.Aborted;
}
}
}
if (status == LoadStatus.InvalidData)
{
if (IsHtmlPage(uri))
{
repository.LoadDefaultSettings();
repository.CurrentSettings.Browser.StartUrl = uri.AbsoluteUri;
logger.Info($"The specified URI '{uri.AbsoluteUri}' appears to point to a HTML page, setting it as startup URL.");
return OperationResult.Success;
}
logger.Error($"The specified settings resource '{uri.AbsoluteUri}' is invalid!");
}
return status == LoadStatus.Success ? OperationResult.Success : OperationResult.Failed;
}
private bool IsHtmlPage(Uri uri)
{
// TODO
return false;
}
private bool TryGetAdminPassword(out string password)
{
password = default(string);
// TODO
return true;
}
private bool TryGetSettingsPassword(out string password)
{
password = default(string);
// TODO
return true;
}
private bool TryInitializeSettingsUri(out Uri uri)
{ {
var path = string.Empty; var path = string.Empty;
var isValidUri = false; var isValidUri = false;
@ -119,19 +214,14 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
return isValidUri; return isValidUri;
} }
private bool LoadSettings(Uri uri) private bool TryValidateSettingsUri(string path, out Uri uri)
{ {
var abort = false; var isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
var settings = repository.LoadSettings(uri);
if (settings.ConfigurationMode == ConfigurationMode.ConfigureClient) isValidUri &= uri != null && uri.IsFile;
{ isValidUri &= File.Exists(path);
abort = IsConfigurationSufficient();
logger.Info($"The user chose to {(abort ? "abort" : "continue")} after successful client configuration."); return isValidUri;
}
return abort;
} }
private bool IsConfigurationSufficient() private bool IsConfigurationSufficient()
@ -142,5 +232,21 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
return abort == MessageBoxResult.Yes; return abort == MessageBoxResult.Yes;
} }
private void LogOperationResult(OperationResult result)
{
switch (result)
{
case OperationResult.Aborted:
logger.Info("The configuration was aborted by the user.");
break;
case OperationResult.Failed:
logger.Warn("The configuration has failed!");
break;
case OperationResult.Success:
logger.Info("The configuration was successful.");
break;
}
}
} }
} }

View file

@ -9,6 +9,7 @@
using System; using System;
using SafeExamBrowser.Contracts.Behaviour; using SafeExamBrowser.Contracts.Behaviour;
using SafeExamBrowser.Contracts.Behaviour.OperationModel; using SafeExamBrowser.Contracts.Behaviour.OperationModel;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Communication.Proxies; using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
@ -169,13 +170,14 @@ namespace SafeExamBrowser.Runtime.Behaviour
if (result == OperationResult.Failed) if (result == OperationResult.Failed)
{ {
// TODO: Check if message box is rendered on new desktop as well! -> E.g. if settings for reconfiguration are invalid
messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error); messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error);
}
if (!initial) if (!initial)
{ {
logger.Info("Terminating application..."); logger.Info("Terminating application...");
shutdown.Invoke(); shutdown.Invoke();
}
} }
} }
} }
@ -245,10 +247,22 @@ namespace SafeExamBrowser.Runtime.Behaviour
shutdown.Invoke(); shutdown.Invoke();
} }
private void RuntimeHost_ReconfigurationRequested() private void RuntimeHost_ReconfigurationRequested(ReconfigurationEventArgs args)
{ {
logger.Info($"Starting reconfiguration..."); var mode = configuration.CurrentSettings.ConfigurationMode;
StartSession();
if (mode == ConfigurationMode.ConfigureClient)
{
logger.Info($"Accepted request for reconfiguration with '{args.ConfigurationPath}'.");
configuration.ReconfigurationFilePath = args.ConfigurationPath;
StartSession();
}
else
{
logger.Info($"Denied request for reconfiguration with '{args.ConfigurationPath}' due to '{mode}' mode!");
// TODO: configuration.CurrentSession.ClientProxy.InformReconfigurationDenied();
}
} }
private void RuntimeHost_ShutdownRequested() private void RuntimeHost_ShutdownRequested()

View file

@ -7,12 +7,10 @@
*/ */
using System; using System;
using System.Threading.Tasks;
using SafeExamBrowser.Contracts.Communication;
using SafeExamBrowser.Contracts.Communication.Data; using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Events;
using SafeExamBrowser.Contracts.Communication.Hosts; using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Core.Communication.Hosts; using SafeExamBrowser.Core.Communication.Hosts;
@ -27,7 +25,7 @@ namespace SafeExamBrowser.Runtime.Communication
public event CommunicationEventHandler ClientDisconnected; public event CommunicationEventHandler ClientDisconnected;
public event CommunicationEventHandler ClientReady; public event CommunicationEventHandler ClientReady;
public event CommunicationEventHandler ReconfigurationRequested; public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
public event CommunicationEventHandler ShutdownRequested; public event CommunicationEventHandler ShutdownRequested;
public RuntimeHost(string address, IConfigurationRepository configuration, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger) public RuntimeHost(string address, IConfigurationRepository configuration, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger)
@ -64,9 +62,9 @@ namespace SafeExamBrowser.Runtime.Communication
{ {
switch (message) switch (message)
{ {
case ReconfigurationMessage reconfigurationMessage: case ReconfigurationMessage r:
// TODO: Not the job of the host, fire event or alike! ReconfigurationRequested?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = r.ConfigurationPath });
return Handle(reconfigurationMessage); return new SimpleResponse(SimpleResponsePurport.Acknowledged);
} }
return new SimpleResponse(SimpleResponsePurport.UnknownMessage); return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
@ -89,22 +87,5 @@ namespace SafeExamBrowser.Runtime.Communication
return new SimpleResponse(SimpleResponsePurport.UnknownMessage); return new SimpleResponse(SimpleResponsePurport.UnknownMessage);
} }
private Response Handle(ReconfigurationMessage message)
{
var isExam = configuration.CurrentSettings.ConfigurationMode == ConfigurationMode.Exam;
var isValidUri = Uri.TryCreate(message.ConfigurationUrl, UriKind.Absolute, out _);
var allowed = !isExam && isValidUri;
Logger.Info($"Received reconfiguration request for '{message.ConfigurationUrl}', {(allowed ? "accepted" : "denied")} it.");
if (allowed)
{
configuration.ReconfigurationUrl = message.ConfigurationUrl;
Task.Run(() => ReconfigurationRequested?.Invoke());
}
return new ReconfigurationResponse { Accepted = allowed };
}
} }
} }

View file

@ -61,8 +61,8 @@
<DebugType>full</DebugType> <DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'"> <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<OutputPath>bin\x86\Release\</OutputPath> <OutputPath>bin\x86\Release\</OutputPath>
@ -71,7 +71,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
<Prefer32Bit>true</Prefer32Bit> <Prefer32Bit>true</Prefer32Bit>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="SimpleWifi, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL"> <Reference Include="SimpleWifi, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">

View file

@ -47,7 +47,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL"> <Reference Include="FontAwesome.WPF, Version=4.7.0.37774, Culture=neutral, PublicKeyToken=0758b07a11a4f466, processorArchitecture=MSIL">

View file

@ -46,7 +46,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -45,7 +45,6 @@
<DebugType>pdbonly</DebugType> <DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
<ErrorReport>prompt</ErrorReport> <ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<Reference Include="System" /> <Reference Include="System" />

View file

@ -9,6 +9,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SafeExamBrowser.Core", "Saf
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A9E6674-2FB4-42EA-85DE-B2445B9AE2D9}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{0A9E6674-2FB4-42EA-85DE-B2445B9AE2D9}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
LICENSE.txt = LICENSE.txt LICENSE.txt = LICENSE.txt
EndProjectSection EndProjectSection
EndProject EndProject