SEBWIN-219: Finished basic implementation of reconfiguration mechanism.

This commit is contained in:
dbuechel 2018-03-08 15:27:12 +01:00
parent 5bd1d9dea4
commit 993329bd71
27 changed files with 259 additions and 98 deletions

View file

@ -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<IApplicationInstance> instances = new List<IApplicationInstance>();
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)

View file

@ -17,7 +17,7 @@ namespace SafeExamBrowser.Browser.Handlers
{
public IResourceHandler Create(IBrowser browser, IFrame frame, string schemeName, IRequest request)
{
var page = "<html><body style=\"height: 90%; display: flex; align-items: center; justify-content: center\"><progress /></body></html>";
var page = @"<html><body style=""height: 90%; display: flex; align-items: center; justify-content: center""><progress /></body></html>";
var handler = ResourceHandler.FromString(page);
return handler;

View file

@ -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);

View file

@ -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()

View file

@ -53,7 +53,7 @@
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
<ItemGroup>
<Compile Include="Session.cs" />
<Compile Include="SessionData.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="ConfigurationRepository.cs" />
<Compile Include="SystemInfo.cs" />

View file

@ -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; }

View file

@ -18,10 +18,12 @@ namespace SafeExamBrowser.Contracts.Communication
/// Defines the API for all communication between the three application components (runtime, service and client).
/// </summary>
[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
{

View file

@ -30,6 +30,11 @@ namespace SafeExamBrowser.Contracts.Communication
/// </summary>
event CommunicationEventHandler ClientReady;
/// <summary>
/// Event fired when the client detected a reconfiguration request.
/// </summary>
event CommunicationEventHandler ReconfigurationRequested;
/// <summary>
/// Event fired when the client requests to shut down the application.
/// </summary>

View file

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

View file

@ -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
{
/// <summary>
/// This message is transmitted to the runtime to request that the application be reconfigured.
/// </summary>
[Serializable]
public class ReconfigurationMessage : Message
{
/// <summary>
/// The locator of the new configuration to be used.
/// </summary>
public string ConfigurationUrl { get; private set; }
public ReconfigurationMessage(string url)
{
ConfigurationUrl = url;
}
}
}

View file

@ -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
{
/// <summary>
/// The response to a <see cref="Messages.ReconfigurationMessage"/>.
/// </summary>
[Serializable]
public class ReconfigurationResponse : Response
{
/// <summary>
/// Indicates whether the reconfiguration request has been accepted.
/// </summary>
public bool Accepted { get; set; }
}
}

View file

@ -16,10 +16,10 @@ namespace SafeExamBrowser.Contracts.Configuration
public interface IConfigurationRepository
{
/// <summary>
/// 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 <c>null</c>!
/// </summary>
ISession CurrentSession { get; }
ISessionData CurrentSession { get; }
/// <summary>
/// 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
/// </summary>
Settings.Settings CurrentSettings { get; }
/// <summary>
/// The locator of the configuration to be used when reconfiguring the application.
/// </summary>
string ReconfigurationUrl { get; set; }
/// <summary>
/// The runtime information for the currently running application instance.
/// </summary>
@ -40,7 +45,7 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// Initializes all relevant data for a new session.
/// </summary>
ISession InitializeSession();
ISessionData InitializeSessionData();
/// <summary>
/// Attempts to load settings from the specified path.

View file

@ -14,7 +14,7 @@ namespace SafeExamBrowser.Contracts.Configuration
/// <summary>
/// Defines all session-related (configuration) data.
/// </summary>
public interface ISession
public interface ISessionData
{
/// <summary>
/// The process information of the client instance associated to this session.

View file

@ -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,

View file

@ -67,18 +67,20 @@
<Compile Include="Communication\IServiceProxy.cs" />
<Compile Include="Communication\Messages\Message.cs" />
<Compile Include="Communication\Messages\DisconnectionMessage.cs" />
<Compile Include="Communication\Messages\ReconfigurationMessage.cs" />
<Compile Include="Communication\Messages\SimpleMessagePurport.cs" />
<Compile Include="Communication\Messages\SimpleMessage.cs" />
<Compile Include="Communication\Responses\AuthenticationResponse.cs" />
<Compile Include="Communication\Responses\ConfigurationResponse.cs" />
<Compile Include="Communication\Responses\ConnectionResponse.cs" />
<Compile Include="Communication\Responses\DisconnectionResponse.cs" />
<Compile Include="Communication\Responses\ReconfigurationResponse.cs" />
<Compile Include="Communication\Responses\Response.cs" />
<Compile Include="Communication\Responses\SimpleResponsePurport.cs" />
<Compile Include="Communication\Responses\SimpleResponse.cs" />
<Compile Include="Configuration\ClientConfiguration.cs" />
<Compile Include="Configuration\RuntimeInfo.cs" />
<Compile Include="Configuration\ISession.cs" />
<Compile Include="Configuration\ISessionData.cs" />
<Compile Include="Configuration\Settings\ConfigurationMode.cs" />
<Compile Include="Behaviour\INotificationController.cs" />
<Compile Include="Behaviour\OperationModel\IOperation.cs" />

View file

@ -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)

View file

@ -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)

View file

@ -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);

View file

@ -12,10 +12,10 @@
<Entry key="MessageBox_ApplicationErrorTitle">
Application Error
</Entry>
<Entry key="MessageBox_ConfigureClientSuccess">
<Entry key="MessageBox_ClientConfigurationQuestion">
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?
</Entry>
<Entry key="MessageBox_ConfigureClientSuccessTitle">
<Entry key="MessageBox_ClientConfigurationQuestionTitle">
Configuration Successful
</Entry>
<Entry key="MessageBox_Quit">
@ -25,15 +25,27 @@
Quit?
</Entry>
<Entry key="MessageBox_QuitError">
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!
</Entry>
<Entry key="MessageBox_QuitErrorTitle">
Quit Error
</Entry>
<Entry key="MessageBox_ReconfigureQuestion">
<Entry key="MessageBox_ReconfigurationDenied">
You are not allowed to reconfigure the application.
</Entry>
<Entry key="MessageBox_ReconfigurationDeniedTitle">
Reconfiguration Denied
</Entry>
<Entry key="MessageBox_ReconfigurationError">
The client failed to communicate the reconfiguration request to the runtime!
</Entry>
<Entry key="MessageBox_ReconfigurationErrorTitle">
Reconfiguration Error
</Entry>
<Entry key="MessageBox_ReconfigurationQuestion">
Would you like to reconfigure the application?
</Entry>
<Entry key="MessageBox_ReconfigureQuestionTitle">
<Entry key="MessageBox_ReconfigurationQuestionTitle">
Configuration Detected
</Entry>
<Entry key="MessageBox_SessionStartError">

View file

@ -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;

View file

@ -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();
}
}
}

View file

@ -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...
}

View file

@ -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.");

View file

@ -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)
{

View file

@ -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 };
}
}
}

View file

@ -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<IOperation>();
var sessionOperations = new Queue<IOperation>();
@ -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);

View file

@ -91,7 +91,7 @@
<Compile Include="Behaviour\Operations\KioskModeOperation.cs" />
<Compile Include="Behaviour\Operations\ServiceConnectionOperation.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceEndOperation.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceOperation.cs" />
<Compile Include="Behaviour\SessionController.cs" />
<Compile Include="Behaviour\Operations\SessionSequenceStartOperation.cs" />
<Compile Include="Communication\RuntimeHost.cs" />
<Compile Include="CompositionRoot.cs" />