SEBWIN-221: Implemented message box communication infrastructure and removed generic session start / stop error messages.

This commit is contained in:
dbuechel 2018-12-14 12:31:31 +01:00
parent 75f5994a3b
commit 4b634d8e99
19 changed files with 305 additions and 28 deletions

View file

@ -160,6 +160,7 @@ namespace SafeExamBrowser.Client
private void RegisterEvents()
{
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
ClientHost.Shutdown += ClientHost_Shutdown;
@ -173,6 +174,7 @@ namespace SafeExamBrowser.Client
private void DeregisterEvents()
{
Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested;
ClientHost.MessageBoxRequested -= ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested -= ClientHost_PasswordRequested;
ClientHost.ReconfigurationDenied -= ClientHost_ReconfigurationDenied;
ClientHost.Shutdown -= ClientHost_Shutdown;
@ -259,6 +261,16 @@ namespace SafeExamBrowser.Client
}
}
private void ClientHost_MessageBoxRequested(MessageBoxRequestEventArgs args)
{
logger.Info($"Received message box request with id '{args.RequestId}'.");
var result = messageBox.Show(args.Message, args.Title, args.Action, args.Icon);
runtime.SubmitMessageBoxResult(args.RequestId, result);
logger.Info($"Message box request with id '{args.RequestId}' yielded result '{result}'.");
}
private void ClientHost_PasswordRequested(PasswordRequestEventArgs args)
{
var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator;

View file

@ -23,6 +23,7 @@ namespace SafeExamBrowser.Client.Communication
public Guid StartupToken { private get; set; }
public bool IsConnected { get; private set; }
public event CommunicationEventHandler<MessageBoxRequestEventArgs> MessageBoxRequested;
public event CommunicationEventHandler<PasswordRequestEventArgs> PasswordRequested;
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationDenied;
public event CommunicationEventHandler RuntimeDisconnected;
@ -62,11 +63,14 @@ namespace SafeExamBrowser.Client.Communication
{
switch (message)
{
case PasswordRequestMessage p:
PasswordRequested?.InvokeAsync(new PasswordRequestEventArgs { Purpose = p.Purpose, RequestId = p.RequestId });
case MessageBoxRequestMessage m:
MessageBoxRequested?.InvokeAsync(new MessageBoxRequestEventArgs { Action = m.Action, Icon = m.Icon, Message = m.Message, RequestId = m.RequestId, Title = m.Title });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ReconfigurationDeniedMessage r:
ReconfigurationDenied?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = r.FilePath });
case PasswordRequestMessage m:
PasswordRequested?.InvokeAsync(new PasswordRequestEventArgs { Purpose = m.Purpose, RequestId = m.RequestId });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ReconfigurationDeniedMessage m:
ReconfigurationDenied?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = m.FilePath });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
}

View file

@ -10,6 +10,7 @@ using System;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Communication.Proxies
{
@ -125,5 +126,31 @@ namespace SafeExamBrowser.Communication.Proxies
return new CommunicationResult(false);
}
}
public CommunicationResult ShowMessage(string message, string title, MessageBoxAction action, MessageBoxIcon icon, Guid requestId)
{
try
{
var response = Send(new MessageBoxRequestMessage(action, icon, message, requestId, title));
var success = IsAcknowledged(response);
if (success)
{
Logger.Debug("Client acknowledged message box request.");
}
else
{
Logger.Error($"Client did not acknowledge message box request! Received: {ToString(response)}.");
}
return new CommunicationResult(success);
}
catch (Exception e)
{
Logger.Error($"Failed to perform '{nameof(ShowMessage)}'", e);
return new CommunicationResult(false);
}
}
}
}

View file

@ -10,6 +10,7 @@ using System;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Proxies;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Communication.Proxies
{
@ -126,6 +127,32 @@ namespace SafeExamBrowser.Communication.Proxies
}
}
public CommunicationResult SubmitMessageBoxResult(Guid requestId, MessageBoxResult result)
{
try
{
var response = Send(new MessageBoxReplyMessage(requestId, result));
var acknowledged = IsAcknowledged(response);
if (acknowledged)
{
Logger.Debug("Runtime acknowledged message box result transmission.");
}
else
{
Logger.Error($"Runtime did not acknowledge message box result transmission! Response: {ToString(response)}.");
}
return new CommunicationResult(acknowledged);
}
catch (Exception e)
{
Logger.Error($"Failed to perform '{nameof(SubmitMessageBoxResult)}'", e);
return new CommunicationResult(false);
}
}
public CommunicationResult SubmitPassword(Guid requestId, bool success, string password = null)
{
try

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/.
*/
using System;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Data
{
/// <summary>
/// The reply to a <see cref="MessageBoxRequestMessage"/>.
/// </summary>
[Serializable]
public class MessageBoxReplyMessage : Message
{
/// <summary>
/// Identifies the message box request.
/// </summary>
public Guid RequestId { get; private set; }
/// <summary>
/// The result of the interaction.
/// </summary>
public MessageBoxResult Result { get; private set; }
public MessageBoxReplyMessage(Guid requestId, MessageBoxResult result)
{
RequestId = requestId;
Result = result;
}
}
}

View file

@ -0,0 +1,54 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Data
{
/// <summary>
/// This message is transmitted to the client to request a message box input by the user.
/// </summary>
[Serializable]
public class MessageBoxRequestMessage : Message
{
/// <summary>
/// The action to be displayed.
/// </summary>
public MessageBoxAction Action { get; private set; }
/// <summary>
/// The icon to be displayed.
/// </summary>
public MessageBoxIcon Icon { get; private set; }
/// <summary>
/// The message to be displayed.
/// </summary>
public string Message { get; private set; }
/// <summary>
/// Identifies the message box request.
/// </summary>
public Guid RequestId { get; private set; }
/// <summary>
/// The title to be displayed.
/// </summary>
public string Title { get; private set; }
public MessageBoxRequestMessage(MessageBoxAction action, MessageBoxIcon icon, string message, Guid requestId, string title)
{
Action = action;
Icon = icon;
Message = message;
RequestId = requestId;
Title = title;
}
}
}

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;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Events
{
/// <summary>
/// The event arguments used for the message box reply event fired by the <see cref="Hosts.IRuntimeHost"/>.
/// </summary>
public class MessageBoxReplyEventArgs : CommunicationEventArgs
{
/// <summary>
/// Identifies the message box request.
/// </summary>
public Guid RequestId { get; set; }
/// <summary>
/// The result of the interaction.
/// </summary>
public MessageBoxResult Result { get; set; }
}
}

View file

@ -0,0 +1,44 @@
/*
* Copyright (c) 2018 ETH Zürich, Educational Development and Technology (LET)
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Events
{
/// <summary>
/// The event arguments used for the message box request event fired by the <see cref="Hosts.IClientHost"/>.
/// </summary>
public class MessageBoxRequestEventArgs : CommunicationEventArgs
{
/// <summary>
/// The action to be displayed.
/// </summary>
public MessageBoxAction Action { get; set; }
/// <summary>
/// The icon to be displayed.
/// </summary>
public MessageBoxIcon Icon { get; set; }
/// <summary>
/// The message to be displayed.
/// </summary>
public string Message { get; set; }
/// <summary>
/// Identifies the message box request.
/// </summary>
public Guid RequestId { get; set; }
/// <summary>
/// The title to be displayed.
/// </summary>
public string Title { get; set; }
}
}

View file

@ -26,6 +26,11 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
/// </summary>
Guid StartupToken { set; }
/// <summary>
/// Event fired when the runtime requests a message box input from the user.
/// </summary>
event CommunicationEventHandler<MessageBoxRequestEventArgs> MessageBoxRequested;
/// <summary>
/// Event fired when the runtime requests a password input from the user.
/// </summary>

View file

@ -41,6 +41,11 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
/// </summary>
event CommunicationEventHandler<ClientConfigurationEventArgs> ClientConfigurationNeeded;
/// <summary>
/// Event fired when the client submitted a message box result chosen by the user.
/// </summary>
event CommunicationEventHandler<MessageBoxReplyEventArgs> MessageBoxReplyReceived;
/// <summary>
/// Event fired when the client submitted a password entered by the user.
/// </summary>

View file

@ -20,6 +20,8 @@ namespace SafeExamBrowser.Contracts.Communication
[ServiceKnownType(typeof(AuthenticationResponse))]
[ServiceKnownType(typeof(ClientConfiguration))]
[ServiceKnownType(typeof(ConfigurationResponse))]
[ServiceKnownType(typeof(MessageBoxReplyMessage))]
[ServiceKnownType(typeof(MessageBoxRequestMessage))]
[ServiceKnownType(typeof(PasswordReplyMessage))]
[ServiceKnownType(typeof(PasswordRequestMessage))]
[ServiceKnownType(typeof(ReconfigurationMessage))]

View file

@ -8,6 +8,7 @@
using System;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Proxies
{
@ -35,5 +36,10 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
/// Requests the client to render a password dialog and subsequently return the interaction result as separate message.
/// </summary>
CommunicationResult RequestPassword(PasswordRequestPurpose purpose, Guid requestId);
/// <summary>
/// Requests the client to render a message box and subsequently return the interaction result as separate message.
/// </summary>
CommunicationResult ShowMessage(string message, string title, MessageBoxAction action, MessageBoxIcon icon, Guid requestId);
}
}

View file

@ -8,6 +8,7 @@
using System;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
namespace SafeExamBrowser.Contracts.Communication.Proxies
{
@ -36,6 +37,11 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
/// </summary>
CommunicationResult RequestReconfiguration(string filePath);
/// <summary>
/// Submits the result of a message box input previously requested by the runtime.
/// </summary>
CommunicationResult SubmitMessageBoxResult(Guid requestId, MessageBoxResult result);
/// <summary>
/// Submits the result of a password input previously requested by the runtime. If the procedure was aborted by the user,
/// the password parameter will be <c>null</c>!

View file

@ -38,10 +38,6 @@ namespace SafeExamBrowser.Contracts.I18n
MessageBox_ReconfigurationErrorTitle,
MessageBox_ReconfigurationQuestion,
MessageBox_ReconfigurationQuestionTitle,
MessageBox_SessionStartError,
MessageBox_SessionStartErrorTitle,
MessageBox_SessionStopError,
MessageBox_SessionStopErrorTitle,
MessageBox_ShutdownError,
MessageBox_ShutdownErrorTitle,
MessageBox_SingleInstance,

View file

@ -52,7 +52,11 @@
<Reference Include="System.ServiceModel" />
</ItemGroup>
<ItemGroup>
<Compile Include="Communication\Data\MessageBoxReplyMessage.cs" />
<Compile Include="Communication\Data\MessageBoxRequestMessage.cs" />
<Compile Include="Communication\Events\ClientConfigurationEventArgs.cs" />
<Compile Include="Communication\Events\MessageBoxReplyEventArgs.cs" />
<Compile Include="Communication\Events\MessageBoxRequestEventArgs.cs" />
<Compile Include="Configuration\Cryptography\EncryptionParameters.cs" />
<Compile Include="Configuration\Cryptography\PasswordParameters.cs" />
<Compile Include="Configuration\Cryptography\PublicKeyHashParameters.cs" />

View file

@ -72,18 +72,6 @@
<Entry key="MessageBox_ReconfigurationQuestionTitle">
Configuration Detected
</Entry>
<Entry key="MessageBox_SessionStartError">
The application failed to start a new session. Please consult the application log for more information...
</Entry>
<Entry key="MessageBox_SessionStartErrorTitle">
Session Start Error
</Entry>
<Entry key="MessageBox_SessionStopError">
The application failed to properly stop the running session. Please consult the application log for more information...
</Entry>
<Entry key="MessageBox_SessionStopErrorTitle">
Session Stop Error
</Entry>
<Entry key="MessageBox_ShutdownError">
An unexpected error occurred during the shutdown procedure! Please consult the application log for more information...
</Entry>

View file

@ -23,6 +23,7 @@ namespace SafeExamBrowser.Runtime.Communication
public event CommunicationEventHandler ClientDisconnected;
public event CommunicationEventHandler ClientReady;
public event CommunicationEventHandler<ClientConfigurationEventArgs> ClientConfigurationNeeded;
public event CommunicationEventHandler<MessageBoxReplyEventArgs> MessageBoxReplyReceived;
public event CommunicationEventHandler<PasswordReplyEventArgs> PasswordReceived;
public event CommunicationEventHandler<ReconfigurationEventArgs> ReconfigurationRequested;
public event CommunicationEventHandler ShutdownRequested;
@ -53,11 +54,14 @@ namespace SafeExamBrowser.Runtime.Communication
{
switch (message)
{
case PasswordReplyMessage r:
PasswordReceived?.InvokeAsync(new PasswordReplyEventArgs { Password = r.Password, RequestId = r.RequestId, Success = r.Success });
case MessageBoxReplyMessage m:
MessageBoxReplyReceived?.InvokeAsync(new MessageBoxReplyEventArgs { RequestId = m.RequestId, Result = m.Result });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ReconfigurationMessage r:
ReconfigurationRequested?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = r.ConfigurationPath });
case PasswordReplyMessage m:
PasswordReceived?.InvokeAsync(new PasswordReplyEventArgs { Password = m.Password, RequestId = m.RequestId, Success = m.Success });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ReconfigurationMessage m:
ReconfigurationRequested?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = m.ConfigurationPath });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
}

View file

@ -273,6 +273,7 @@ namespace SafeExamBrowser.Runtime.Operations
else
{
logger.Info("Authentication has failed!");
ActionRequired?.Invoke(new InvalidPasswordMessageArgs());
return OperationResult.Failed;
}

View file

@ -207,9 +207,7 @@ namespace SafeExamBrowser.Runtime
{
StopSession();
messageBox.Show(TextKey.MessageBox_SessionStartError, TextKey.MessageBox_SessionStartErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
logger.Info("Terminating application...");
shutdown.Invoke();
}
}
@ -247,7 +245,6 @@ namespace SafeExamBrowser.Runtime
else
{
logger.Info("### --- Session Stop Failed --- ###");
messageBox.Show(TextKey.MessageBox_SessionStopError, TextKey.MessageBox_SessionStopErrorTitle, icon: MessageBoxIcon.Error, parent: runtimeWindow);
}
}
@ -419,10 +416,40 @@ namespace SafeExamBrowser.Runtime
}
else
{
// TODO
ShowMessageBoxViaClient(message, title, MessageBoxAction.Confirm, args.Icon);
}
}
private MessageBoxResult ShowMessageBoxViaClient(string message, string title, MessageBoxAction confirm, MessageBoxIcon icon)
{
var requestId = Guid.NewGuid();
var result = MessageBoxResult.None;
var response = default(MessageBoxReplyEventArgs);
var responseEvent = new AutoResetEvent(false);
var responseEventHandler = new CommunicationEventHandler<MessageBoxReplyEventArgs>((args) =>
{
if (args.RequestId == requestId)
{
response = args;
responseEvent.Set();
}
});
runtimeHost.MessageBoxReplyReceived += responseEventHandler;
var communication = sessionContext.ClientProxy.ShowMessage(message, title, MessageBoxAction.Confirm, icon, requestId);
if (communication.Success)
{
responseEvent.WaitOne();
result = response.Result;
}
runtimeHost.MessageBoxReplyReceived -= responseEventHandler;
return result;
}
private void TryGetPasswordViaDialog(PasswordRequiredEventArgs args)
{
var isAdmin = args.Purpose == PasswordRequestPurpose.Administrator;