From 993329bd71bfc5cad6c8e593b8fabe6589c9da60 Mon Sep 17 00:00:00 2001 From: dbuechel Date: Thu, 8 Mar 2018 15:27:12 +0100 Subject: [PATCH] SEBWIN-219: Finished basic implementation of reconfiguration mechanism. --- .../BrowserApplicationController.cs | 39 +++++++++++++++---- .../Handlers/SchemeHandlerFactory.cs | 2 +- SafeExamBrowser.Client/CompositionRoot.cs | 6 ++- .../ConfigurationRepository.cs | 7 ++-- .../SafeExamBrowser.Configuration.csproj | 2 +- .../{Session.cs => SessionData.cs} | 2 +- .../Communication/ICommunication.cs | 6 ++- .../Communication/IRuntimeHost.cs | 5 +++ .../Communication/IRuntimeProxy.cs | 7 ++++ .../Messages/ReconfigurationMessage.cs | 29 ++++++++++++++ .../Responses/ReconfigurationResponse.cs | 24 ++++++++++++ .../Configuration/IConfigurationRepository.cs | 11 ++++-- .../{ISession.cs => ISessionData.cs} | 2 +- SafeExamBrowser.Contracts/I18n/TextKey.cs | 12 ++++-- .../SafeExamBrowser.Contracts.csproj | 4 +- .../Communication/BaseHost.cs | 27 +++++++------ .../Communication/BaseProxy.cs | 3 +- .../Communication/RuntimeProxy.cs | 12 ++++++ SafeExamBrowser.Core/I18n/Text.xml | 22 ++++++++--- .../Operations/ConfigurationOperation.cs | 4 +- .../Operations/SessionSequenceEndOperation.cs | 37 +++++++++--------- .../SessionSequenceStartOperation.cs | 30 +++++++------- .../Behaviour/RuntimeController.cs | 10 +++++ ...uenceOperation.cs => SessionController.cs} | 20 ++++------ .../Communication/RuntimeHost.cs | 27 +++++++++++++ SafeExamBrowser.Runtime/CompositionRoot.cs | 5 ++- .../SafeExamBrowser.Runtime.csproj | 2 +- 27 files changed, 259 insertions(+), 98 deletions(-) rename SafeExamBrowser.Configuration/{Session.cs => SessionData.cs} (93%) create mode 100644 SafeExamBrowser.Contracts/Communication/Messages/ReconfigurationMessage.cs create mode 100644 SafeExamBrowser.Contracts/Communication/Responses/ReconfigurationResponse.cs rename SafeExamBrowser.Contracts/Configuration/{ISession.cs => ISessionData.cs} (96%) rename SafeExamBrowser.Runtime/Behaviour/{Operations/SessionSequenceOperation.cs => SessionController.cs} (92%) diff --git a/SafeExamBrowser.Browser/BrowserApplicationController.cs b/SafeExamBrowser.Browser/BrowserApplicationController.cs index 20901d10..cac570e6 100644 --- a/SafeExamBrowser.Browser/BrowserApplicationController.cs +++ b/SafeExamBrowser.Browser/BrowserApplicationController.cs @@ -13,8 +13,10 @@ using System.Linq; using CefSharp; using SafeExamBrowser.Browser.Handlers; using SafeExamBrowser.Contracts.Behaviour; +using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.I18n; +using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.UserInterface.Taskbar; using BrowserSettings = SafeExamBrowser.Contracts.Configuration.Settings.BrowserSettings; @@ -26,6 +28,8 @@ namespace SafeExamBrowser.Browser private IApplicationButton button; private IList instances = new List(); private BrowserSettings settings; + private ILogger logger; + private IRuntimeProxy runtime; private RuntimeInfo runtimeInfo; private IUserInterfaceFactory uiFactory; private IText text; @@ -33,9 +37,13 @@ namespace SafeExamBrowser.Browser public BrowserApplicationController( BrowserSettings settings, RuntimeInfo runtimeInfo, + ILogger logger, + IRuntimeProxy runtime, IText text, IUserInterfaceFactory uiFactory) { + this.logger = logger; + this.runtime = runtime; this.runtimeInfo = runtimeInfo; this.settings = settings; this.text = text; @@ -112,15 +120,32 @@ namespace SafeExamBrowser.Browser private void Instance_ConfigurationDetected(string url, CancelEventArgs args) { - // TODO: - // 1. Ask whether reconfiguration should be attempted - // 2. Contact runtime and ask whether configuration valid and reconfiguration allowed - // - If yes, do nothing and wait for shutdown command - // - If no, show message box and NAVIGATE TO PREVIOUS PAGE -> but how? + var result = uiFactory.Show(TextKey.MessageBox_ReconfigurationQuestion, TextKey.MessageBox_ReconfigurationQuestionTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question); + var reconfigure = result == MessageBoxResult.Yes; + var allowed = false; - var result = uiFactory.Show(TextKey.MessageBox_ReconfigureQuestion, TextKey.MessageBox_ReconfigureQuestionTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question); + logger.Info($"Detected configuration request for '{url}'. The user chose to {(reconfigure ? "start" : "abort")} the reconfiguration."); - args.Cancel = result == MessageBoxResult.No; + if (reconfigure) + { + try + { + allowed = runtime.RequestReconfiguration(url); + logger.Info($"The runtime {(allowed ? "accepted" : "denied")} the reconfiguration request."); + + if (!allowed) + { + uiFactory.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle); + } + } + catch (Exception e) + { + logger.Error("Failed to communicate the reconfiguration request to the runtime!", e); + uiFactory.Show(TextKey.MessageBox_ReconfigurationError, TextKey.MessageBox_ReconfigurationErrorTitle, icon: MessageBoxIcon.Error); + } + } + + args.Cancel = !allowed; } private void Instance_Terminated(Guid id) diff --git a/SafeExamBrowser.Browser/Handlers/SchemeHandlerFactory.cs b/SafeExamBrowser.Browser/Handlers/SchemeHandlerFactory.cs index 9d13bbb6..ba928001 100644 --- a/SafeExamBrowser.Browser/Handlers/SchemeHandlerFactory.cs +++ b/SafeExamBrowser.Browser/Handlers/SchemeHandlerFactory.cs @@ -17,7 +17,7 @@ namespace SafeExamBrowser.Browser.Handlers { public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request) { - var page = ""; + var page = @""; var handler = ResourceHandler.FromString(page); return handler; diff --git a/SafeExamBrowser.Client/CompositionRoot.cs b/SafeExamBrowser.Client/CompositionRoot.cs index 395e0a9a..e8ea3b22 100644 --- a/SafeExamBrowser.Client/CompositionRoot.cs +++ b/SafeExamBrowser.Client/CompositionRoot.cs @@ -48,6 +48,7 @@ namespace SafeExamBrowser.Client private IClientHost clientHost; private ILogger logger; private INativeMethods nativeMethods; + private IRuntimeProxy runtimeProxy; private ISystemInfo systemInfo; private IText text; private IUserInterfaceFactory uiFactory; @@ -68,8 +69,8 @@ namespace SafeExamBrowser.Client text = new Text(logger); uiFactory = new UserInterfaceFactory(text); + runtimeProxy = new RuntimeProxy(runtimeHostUri, new ModuleLogger(logger, typeof(RuntimeProxy))); - var runtimeProxy = new RuntimeProxy(runtimeHostUri, new ModuleLogger(logger, typeof(RuntimeProxy))); var displayMonitor = new DisplayMonitor(new ModuleLogger(logger, typeof(DisplayMonitor)), nativeMethods); var processMonitor = new ProcessMonitor(new ModuleLogger(logger, typeof(ProcessMonitor)), nativeMethods); var windowMonitor = new WindowMonitor(new ModuleLogger(logger, typeof(WindowMonitor)), nativeMethods); @@ -143,7 +144,8 @@ namespace SafeExamBrowser.Client private IOperation BuildBrowserOperation() { - var browserController = new BrowserApplicationController(configuration.Settings.Browser, configuration.RuntimeInfo, text, uiFactory); + var moduleLogger = new ModuleLogger(logger, typeof(BrowserApplicationController)); + var browserController = new BrowserApplicationController(configuration.Settings.Browser, configuration.RuntimeInfo, moduleLogger, runtimeProxy, text, uiFactory); var browserInfo = new BrowserApplicationInfo(); var operation = new BrowserOperation(browserController, browserInfo, logger, Taskbar, uiFactory); diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs index f309a6d7..496df28c 100644 --- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs +++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs @@ -18,8 +18,9 @@ namespace SafeExamBrowser.Configuration { private RuntimeInfo runtimeInfo; - public ISession CurrentSession { get; private set; } + public ISessionData CurrentSession { get; private set; } public Settings CurrentSettings { get; private set; } + public string ReconfigurationUrl { get; set; } public RuntimeInfo RuntimeInfo { @@ -34,9 +35,9 @@ namespace SafeExamBrowser.Configuration } } - public ISession InitializeSession() + public ISessionData InitializeSessionData() { - var session = new Session + var session = new SessionData { Id = Guid.NewGuid(), StartupToken = Guid.NewGuid() diff --git a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj index 220d96e9..9462738c 100644 --- a/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj +++ b/SafeExamBrowser.Configuration/SafeExamBrowser.Configuration.csproj @@ -53,7 +53,7 @@ - + diff --git a/SafeExamBrowser.Configuration/Session.cs b/SafeExamBrowser.Configuration/SessionData.cs similarity index 93% rename from SafeExamBrowser.Configuration/Session.cs rename to SafeExamBrowser.Configuration/SessionData.cs index 9291c398..89d0c1e4 100644 --- a/SafeExamBrowser.Configuration/Session.cs +++ b/SafeExamBrowser.Configuration/SessionData.cs @@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.WindowsApi; namespace SafeExamBrowser.Configuration { - public class Session : ISession + public class SessionData : ISessionData { public IProcess ClientProcess { get; set; } public Guid Id { get; set; } diff --git a/SafeExamBrowser.Contracts/Communication/ICommunication.cs b/SafeExamBrowser.Contracts/Communication/ICommunication.cs index 9bab6eb5..5448dde9 100644 --- a/SafeExamBrowser.Contracts/Communication/ICommunication.cs +++ b/SafeExamBrowser.Contracts/Communication/ICommunication.cs @@ -18,10 +18,12 @@ namespace SafeExamBrowser.Contracts.Communication /// Defines the API for all communication between the three application components (runtime, service and client). /// [ServiceContract(SessionMode = SessionMode.Required)] - [ServiceKnownType(typeof(SimpleMessage))] [ServiceKnownType(typeof(AuthenticationResponse))] - [ServiceKnownType(typeof(ConfigurationResponse))] [ServiceKnownType(typeof(ClientConfiguration))] + [ServiceKnownType(typeof(ConfigurationResponse))] + [ServiceKnownType(typeof(ReconfigurationMessage))] + [ServiceKnownType(typeof(ReconfigurationResponse))] + [ServiceKnownType(typeof(SimpleMessage))] [ServiceKnownType(typeof(SimpleResponse))] public interface ICommunication { diff --git a/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs b/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs index 49610487..b1f5df31 100644 --- a/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs +++ b/SafeExamBrowser.Contracts/Communication/IRuntimeHost.cs @@ -30,6 +30,11 @@ namespace SafeExamBrowser.Contracts.Communication /// event CommunicationEventHandler ClientReady; + /// + /// Event fired when the client detected a reconfiguration request. + /// + event CommunicationEventHandler ReconfigurationRequested; + /// /// Event fired when the client requests to shut down the application. /// diff --git a/SafeExamBrowser.Contracts/Communication/IRuntimeProxy.cs b/SafeExamBrowser.Contracts/Communication/IRuntimeProxy.cs index 5b5ba4a9..3aa7c16d 100644 --- a/SafeExamBrowser.Contracts/Communication/IRuntimeProxy.cs +++ b/SafeExamBrowser.Contracts/Communication/IRuntimeProxy.cs @@ -32,5 +32,12 @@ namespace SafeExamBrowser.Contracts.Communication /// /// If the communication failed. void RequestShutdown(); + + /// + /// Requests the runtime to reconfigure the application with the configuration from the given location. Returns true if + /// the runtime accepted the request, otherwise false. + /// + /// If the communication failed. + bool RequestReconfiguration(string url); } } diff --git a/SafeExamBrowser.Contracts/Communication/Messages/ReconfigurationMessage.cs b/SafeExamBrowser.Contracts/Communication/Messages/ReconfigurationMessage.cs new file mode 100644 index 00000000..fdd9d0b2 --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/Messages/ReconfigurationMessage.cs @@ -0,0 +1,29 @@ +/* + * 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; + +namespace SafeExamBrowser.Contracts.Communication.Messages +{ + /// + /// This message is transmitted to the runtime to request that the application be reconfigured. + /// + [Serializable] + public class ReconfigurationMessage : Message + { + /// + /// The locator of the new configuration to be used. + /// + public string ConfigurationUrl { get; private set; } + + public ReconfigurationMessage(string url) + { + ConfigurationUrl = url; + } + } +} diff --git a/SafeExamBrowser.Contracts/Communication/Responses/ReconfigurationResponse.cs b/SafeExamBrowser.Contracts/Communication/Responses/ReconfigurationResponse.cs new file mode 100644 index 00000000..92d5f6bb --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/Responses/ReconfigurationResponse.cs @@ -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 System; + +namespace SafeExamBrowser.Contracts.Communication.Responses +{ + /// + /// The response to a . + /// + [Serializable] + public class ReconfigurationResponse : Response + { + /// + /// Indicates whether the reconfiguration request has been accepted. + /// + public bool Accepted { get; set; } + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs index 78bec895..71913a3e 100644 --- a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs +++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs @@ -16,10 +16,10 @@ namespace SafeExamBrowser.Contracts.Configuration public interface IConfigurationRepository { /// - /// Retrieves the current session, i.e. the last one which was initialized. If no session has been initialized yet, this + /// Retrieves the current session data, i.e. the last one which was initialized. If no session has been initialized yet, this /// property will be null! /// - ISession CurrentSession { get; } + ISessionData CurrentSession { get; } /// /// Retrieves the current settings, i.e. the last ones which were loaded. If no settings have been loaded yet, this property will @@ -27,6 +27,11 @@ namespace SafeExamBrowser.Contracts.Configuration /// Settings.Settings CurrentSettings { get; } + /// + /// The locator of the configuration to be used when reconfiguring the application. + /// + string ReconfigurationUrl { get; set; } + /// /// The runtime information for the currently running application instance. /// @@ -40,7 +45,7 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// Initializes all relevant data for a new session. /// - ISession InitializeSession(); + ISessionData InitializeSessionData(); /// /// Attempts to load settings from the specified path. diff --git a/SafeExamBrowser.Contracts/Configuration/ISession.cs b/SafeExamBrowser.Contracts/Configuration/ISessionData.cs similarity index 96% rename from SafeExamBrowser.Contracts/Configuration/ISession.cs rename to SafeExamBrowser.Contracts/Configuration/ISessionData.cs index f14ae60c..2455fdbc 100644 --- a/SafeExamBrowser.Contracts/Configuration/ISession.cs +++ b/SafeExamBrowser.Contracts/Configuration/ISessionData.cs @@ -14,7 +14,7 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// Defines all session-related (configuration) data. /// - public interface ISession + public interface ISessionData { /// /// The process information of the client instance associated to this session. diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs index 94b225bf..d5cdd1a9 100644 --- a/SafeExamBrowser.Contracts/I18n/TextKey.cs +++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs @@ -18,14 +18,18 @@ namespace SafeExamBrowser.Contracts.I18n LogWindow_Title, MessageBox_ApplicationError, MessageBox_ApplicationErrorTitle, - MessageBox_ConfigureClientSuccess, - MessageBox_ConfigureClientSuccessTitle, + MessageBox_ClientConfigurationQuestion, + MessageBox_ClientConfigurationQuestionTitle, MessageBox_Quit, MessageBox_QuitTitle, MessageBox_QuitError, MessageBox_QuitErrorTitle, - MessageBox_ReconfigureQuestion, - MessageBox_ReconfigureQuestionTitle, + MessageBox_ReconfigurationDenied, + MessageBox_ReconfigurationDeniedTitle, + MessageBox_ReconfigurationError, + MessageBox_ReconfigurationErrorTitle, + MessageBox_ReconfigurationQuestion, + MessageBox_ReconfigurationQuestionTitle, MessageBox_SessionStartError, MessageBox_SessionStartErrorTitle, MessageBox_SessionStopError, diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 412ca807..1336c27c 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -67,18 +67,20 @@ + + - + diff --git a/SafeExamBrowser.Core/Communication/BaseHost.cs b/SafeExamBrowser.Core/Communication/BaseHost.cs index 8feb1ecd..45343238 100644 --- a/SafeExamBrowser.Core/Communication/BaseHost.cs +++ b/SafeExamBrowser.Core/Communication/BaseHost.cs @@ -26,7 +26,6 @@ namespace SafeExamBrowser.Core.Communication private readonly object @lock = new object(); private string address; - private ILogger logger; private ServiceHost host; private Thread hostThread; @@ -47,7 +46,7 @@ namespace SafeExamBrowser.Core.Communication public BaseHost(string address, ILogger logger) { this.address = address; - this.logger = logger; + this.Logger = logger; } protected abstract bool OnConnect(Guid? token); @@ -59,7 +58,7 @@ namespace SafeExamBrowser.Core.Communication { lock (@lock) { - logger.Debug($"Received connection request with authentication token '{token}'."); + Logger.Debug($"Received connection request with authentication token '{token}'."); var response = new ConnectionResponse(); var connected = OnConnect(token); @@ -70,7 +69,7 @@ namespace SafeExamBrowser.Core.Communication response.ConnectionEstablished = true; } - logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request."); + Logger.Debug($"{(connected ? "Accepted" : "Denied")} connection request."); return response; } @@ -82,7 +81,7 @@ namespace SafeExamBrowser.Core.Communication { var response = new DisconnectionResponse(); - logger.Debug($"Received disconnection request with message '{ToString(message)}'."); + Logger.Debug($"Received disconnection request with message '{ToString(message)}'."); if (IsAuthorized(message?.CommunicationToken)) { @@ -118,7 +117,7 @@ namespace SafeExamBrowser.Core.Communication } } - logger.Debug($"Received message '{ToString(message)}', sending response '{ToString(response)}'."); + Logger.Debug($"Received message '{ToString(message)}', sending response '{ToString(response)}'."); return response; } @@ -153,7 +152,7 @@ namespace SafeExamBrowser.Core.Communication if (success) { - logger.Debug($"Terminated communication host for endpoint '{address}'."); + Logger.Debug($"Terminated communication host for endpoint '{address}'."); } else { @@ -183,7 +182,7 @@ namespace SafeExamBrowser.Core.Communication host.UnknownMessageReceived += Host_UnknownMessageReceived; host.Open(); - logger.Debug($"Successfully started communication host for endpoint '{address}'."); + Logger.Debug($"Successfully started communication host for endpoint '{address}'."); startedEvent.Set(); } @@ -215,32 +214,32 @@ namespace SafeExamBrowser.Core.Communication private void Host_Closed(object sender, EventArgs e) { - logger.Debug("Communication host has been closed."); + Logger.Debug("Communication host has been closed."); } private void Host_Closing(object sender, EventArgs e) { - logger.Debug("Communication host is closing..."); + Logger.Debug("Communication host is closing..."); } private void Host_Faulted(object sender, EventArgs e) { - logger.Error("Communication host has faulted!"); + Logger.Error("Communication host has faulted!"); } private void Host_Opened(object sender, EventArgs e) { - logger.Debug("Communication host has been opened."); + Logger.Debug("Communication host has been opened."); } private void Host_Opening(object sender, EventArgs e) { - logger.Debug("Communication host is opening..."); + Logger.Debug("Communication host is opening..."); } private void Host_UnknownMessageReceived(object sender, UnknownMessageReceivedEventArgs e) { - logger.Warn($"Communication host has received an unknown message: {e?.Message}."); + Logger.Warn($"Communication host has received an unknown message: {e?.Message}."); } private string ToString(Message message) diff --git a/SafeExamBrowser.Core/Communication/BaseProxy.cs b/SafeExamBrowser.Core/Communication/BaseProxy.cs index 0c85b28e..f29a03ca 100644 --- a/SafeExamBrowser.Core/Communication/BaseProxy.cs +++ b/SafeExamBrowser.Core/Communication/BaseProxy.cs @@ -51,7 +51,7 @@ namespace SafeExamBrowser.Core.Communication (channel as ICommunicationObject).Opened += BaseProxy_Opened; (channel as ICommunicationObject).Opening += BaseProxy_Opening; - Logger.Debug($"Trying to connect to endpoint {address}{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}..."); + Logger.Debug($"Trying to connect to endpoint '{address}'{(token.HasValue ? $" with authentication token '{token}'" : string.Empty)}..."); var response = channel.Connect(token); @@ -125,7 +125,6 @@ namespace SafeExamBrowser.Core.Communication private void BaseProxy_Faulted(object sender, EventArgs e) { Logger.Error("Communication channel has faulted!"); - ConnectionLost?.Invoke(); } private void BaseProxy_Opened(object sender, EventArgs e) diff --git a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs index fb1cfe9f..0aff2402 100644 --- a/SafeExamBrowser.Core/Communication/RuntimeProxy.cs +++ b/SafeExamBrowser.Core/Communication/RuntimeProxy.cs @@ -46,6 +46,18 @@ namespace SafeExamBrowser.Core.Communication } } + public bool RequestReconfiguration(string url) + { + var response = Send(new ReconfigurationMessage(url)); + + if (response is ReconfigurationResponse reconfiguration) + { + return reconfiguration.Accepted; + } + + return false; + } + public void RequestShutdown() { var response = Send(SimpleMessagePurport.RequestShutdown); diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml index 492d40a0..5247dae9 100644 --- a/SafeExamBrowser.Core/I18n/Text.xml +++ b/SafeExamBrowser.Core/I18n/Text.xml @@ -12,10 +12,10 @@ Application Error - + The client configuration has been saved and will be used when you start the application the next time. Do you want to quit for now? - + Configuration Successful @@ -25,15 +25,27 @@ Quit? - The client failed to communicate the shutdown request to the runtime! Please try again... + The client failed to communicate the shutdown request to the runtime! Quit Error - + + You are not allowed to reconfigure the application. + + + Reconfiguration Denied + + + The client failed to communicate the reconfiguration request to the runtime! + + + Reconfiguration Error + + Would you like to reconfigure the application? - + Configuration Detected diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs index c9bb0ae8..7fe37d6e 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs @@ -127,8 +127,8 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations private bool IsConfigurationSufficient() { - var message = text.Get(TextKey.MessageBox_ConfigureClientSuccess); - var title = text.Get(TextKey.MessageBox_ConfigureClientSuccessTitle); + var message = text.Get(TextKey.MessageBox_ClientConfigurationQuestion); + var title = text.Get(TextKey.MessageBox_ClientConfigurationQuestionTitle); var abort = uiFactory.Show(message, title, MessageBoxAction.YesNo, MessageBoxIcon.Question); return abort == MessageBoxResult.Yes; diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceEndOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceEndOperation.cs index 80c0bccd..fcab23df 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceEndOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceEndOperation.cs @@ -7,38 +7,39 @@ */ using SafeExamBrowser.Contracts.Behaviour.OperationModel; -using SafeExamBrowser.Contracts.Communication; -using SafeExamBrowser.Contracts.Configuration; -using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.WindowsApi; +using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.Runtime.Behaviour.Operations { - internal class SessionSequenceEndOperation : SessionSequenceOperation + internal class SessionSequenceEndOperation : IOperation { - public SessionSequenceEndOperation( - IClientProxy client, - IConfigurationRepository configuration, - ILogger logger, - IProcessFactory processFactory, - IRuntimeHost runtimeHost, - IServiceProxy service) : base(client, configuration, logger, processFactory, runtimeHost, service) + private SessionController controller; + + public IProgressIndicator ProgressIndicator { private get; set; } + + public SessionSequenceEndOperation(SessionController controller) { + this.controller = controller; } - public override OperationResult Perform() + public OperationResult Perform() { - return StartSession(); + controller.ProgressIndicator = ProgressIndicator; + + return controller.StartSession(); } - public override OperationResult Repeat() + public OperationResult Repeat() { - return StartSession(); + controller.ProgressIndicator = ProgressIndicator; + + return controller.StartSession(); } - public override void Revert() + public void Revert() { - StopSession(); + controller.ProgressIndicator = ProgressIndicator; + controller.StopSession(); } } } diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceStartOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceStartOperation.cs index e0324b3c..9fded678 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceStartOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceStartOperation.cs @@ -7,36 +7,34 @@ */ using SafeExamBrowser.Contracts.Behaviour.OperationModel; -using SafeExamBrowser.Contracts.Communication; -using SafeExamBrowser.Contracts.Configuration; -using SafeExamBrowser.Contracts.Logging; -using SafeExamBrowser.Contracts.WindowsApi; +using SafeExamBrowser.Contracts.UserInterface; namespace SafeExamBrowser.Runtime.Behaviour.Operations { - internal class SessionSequenceStartOperation : SessionSequenceOperation + internal class SessionSequenceStartOperation : IOperation { - public SessionSequenceStartOperation( - IClientProxy client, - IConfigurationRepository configuration, - ILogger logger, - IProcessFactory processFactory, - IRuntimeHost runtimeHost, - IServiceProxy service) : base(client, configuration, logger, processFactory, runtimeHost, service) + private SessionController controller; + + public IProgressIndicator ProgressIndicator { private get; set; } + + public SessionSequenceStartOperation(SessionController controller) { + this.controller = controller; } - public override OperationResult Perform() + public OperationResult Perform() { return OperationResult.Success; } - public override OperationResult Repeat() + public OperationResult Repeat() { - return StopSession(); + controller.ProgressIndicator = ProgressIndicator; + + return controller.StopSession(); } - public override void Revert() + public void Revert() { // Nothing to do here... } diff --git a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs index 030016ef..25ffc87c 100644 --- a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs +++ b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs @@ -134,6 +134,7 @@ namespace SafeExamBrowser.Runtime.Behaviour { runtimeWindow.Show(); runtimeWindow.BringToForeground(); + runtimeWindow.ShowProgressBar(); logger.Info(">>>--- Initiating session procedure ---<<<"); if (sessionRunning) @@ -170,6 +171,7 @@ namespace SafeExamBrowser.Runtime.Behaviour if (!initial) { + logger.Info("Terminating application..."); shutdown.Invoke(); } } @@ -201,6 +203,7 @@ namespace SafeExamBrowser.Runtime.Behaviour private void RegisterEvents() { client.ConnectionLost += Client_ConnectionLost; + runtimeHost.ReconfigurationRequested += RuntimeHost_ReconfigurationRequested; runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested; } @@ -212,6 +215,7 @@ namespace SafeExamBrowser.Runtime.Behaviour private void DeregisterEvents() { client.ConnectionLost -= Client_ConnectionLost; + runtimeHost.ReconfigurationRequested -= RuntimeHost_ReconfigurationRequested; runtimeHost.ShutdownRequested -= RuntimeHost_ShutdownRequested; } @@ -238,6 +242,12 @@ namespace SafeExamBrowser.Runtime.Behaviour shutdown.Invoke(); } + private void RuntimeHost_ReconfigurationRequested() + { + logger.Info($"Starting reconfiguration..."); + StartSession(); + } + private void RuntimeHost_ShutdownRequested() { logger.Info("Received shutdown request from the client application."); diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs b/SafeExamBrowser.Runtime/Behaviour/SessionController.cs similarity index 92% rename from SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs rename to SafeExamBrowser.Runtime/Behaviour/SessionController.cs index 222eadf7..407ad16c 100644 --- a/SafeExamBrowser.Runtime/Behaviour/Operations/SessionSequenceOperation.cs +++ b/SafeExamBrowser.Runtime/Behaviour/SessionController.cs @@ -15,9 +15,9 @@ using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.UserInterface; using SafeExamBrowser.Contracts.WindowsApi; -namespace SafeExamBrowser.Runtime.Behaviour.Operations +namespace SafeExamBrowser.Runtime.Behaviour { - internal abstract class SessionSequenceOperation : IOperation + internal class SessionController { private const int TEN_SECONDS = 10000; @@ -28,11 +28,11 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations private IProcessFactory processFactory; private IRuntimeHost runtimeHost; private IServiceProxy service; - private ISession session; + private ISessionData session; - public IProgressIndicator ProgressIndicator { private get; set; } + internal IProgressIndicator ProgressIndicator { private get; set; } - public SessionSequenceOperation( + internal SessionController( IClientProxy client, IConfigurationRepository configuration, ILogger logger, @@ -48,16 +48,12 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations this.service = service; } - public abstract OperationResult Perform(); - public abstract OperationResult Repeat(); - public abstract void Revert(); - - protected OperationResult StartSession() + internal OperationResult StartSession() { logger.Info("Starting new session..."); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartSession, true); - session = configuration.InitializeSession(); + session = configuration.InitializeSessionData(); runtimeHost.StartupToken = session.StartupToken; logger.Info("Initializing service session..."); @@ -78,7 +74,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations return OperationResult.Success; } - protected OperationResult StopSession() + internal OperationResult StopSession() { if (sessionRunning) { diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs index bce73790..4fe1249a 100644 --- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs +++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs @@ -7,10 +7,12 @@ */ using System; +using System.Threading.Tasks; using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Communication.Messages; using SafeExamBrowser.Contracts.Communication.Responses; using SafeExamBrowser.Contracts.Configuration; +using SafeExamBrowser.Contracts.Configuration.Settings; using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Core.Communication; @@ -25,6 +27,7 @@ namespace SafeExamBrowser.Runtime.Communication public event CommunicationEventHandler ClientDisconnected; public event CommunicationEventHandler ClientReady; + public event CommunicationEventHandler ReconfigurationRequested; public event CommunicationEventHandler ShutdownRequested; public RuntimeHost(string address, IConfigurationRepository configuration, ILogger logger) : base(address, logger) @@ -48,10 +51,17 @@ namespace SafeExamBrowser.Runtime.Communication protected override void OnDisconnect() { ClientDisconnected?.Invoke(); + allowConnection = true; } protected override Response OnReceive(Message message) { + switch (message) + { + case ReconfigurationMessage reconfigurationMessage: + return Handle(reconfigurationMessage); + } + return new SimpleResponse(SimpleResponsePurport.UnknownMessage); } @@ -71,5 +81,22 @@ namespace SafeExamBrowser.Runtime.Communication 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 }; + } } } diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index 047c316b..0a7fbe62 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -52,6 +52,7 @@ namespace SafeExamBrowser.Runtime var clientProxy = new ClientProxy(runtimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientProxy))); var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, configuration, new ModuleLogger(logger, typeof(RuntimeHost))); var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy))); + var sessionController = new SessionController(clientProxy, configuration, logger, processFactory, runtimeHost, serviceProxy); var bootstrapOperations = new Queue(); var sessionOperations = new Queue(); @@ -59,11 +60,11 @@ namespace SafeExamBrowser.Runtime bootstrapOperations.Enqueue(new I18nOperation(logger, text)); bootstrapOperations.Enqueue(new CommunicationOperation(runtimeHost, logger)); - sessionOperations.Enqueue(new SessionSequenceStartOperation(clientProxy, configuration, logger, processFactory, runtimeHost, serviceProxy)); + sessionOperations.Enqueue(new SessionSequenceStartOperation(sessionController)); sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, runtimeInfo, text, uiFactory, args)); sessionOperations.Enqueue(new ServiceConnectionOperation(configuration, logger, serviceProxy, text)); sessionOperations.Enqueue(new KioskModeOperation(logger, configuration)); - sessionOperations.Enqueue(new SessionSequenceEndOperation(clientProxy, configuration, logger, processFactory, runtimeHost, serviceProxy)); + sessionOperations.Enqueue(new SessionSequenceEndOperation(sessionController)); var boostrapSequence = new OperationSequence(logger, bootstrapOperations); var sessionSequence = new OperationSequence(logger, sessionOperations); diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj index 91984368..8b75d2e9 100644 --- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj +++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj @@ -91,7 +91,7 @@ - +