SEBWIN-495: Improved error message for prohibited display configuration.

This commit is contained in:
Damian Büchel 2021-06-29 17:34:05 +02:00
parent ee2133c0c2
commit 213f45ad3a
14 changed files with 140 additions and 60 deletions

View file

@ -414,7 +414,7 @@ namespace SafeExamBrowser.Client.UnitTests
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
displayMonitor.Setup(m => m.InitializePrimaryDisplay(It.Is<int>(h => h == height))).Callback(() => workingArea = ++order);
displayMonitor.Setup(m => m.IsAllowedConfiguration(It.IsAny<DisplaySettings>())).Returns(true);
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
@ -445,7 +445,7 @@ namespace SafeExamBrowser.Client.UnitTests
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == 0))).Callback(() => workingArea = ++order);
displayMonitor.Setup(m => m.IsAllowedConfiguration(It.IsAny<DisplaySettings>())).Returns(true);
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);

View file

@ -513,7 +513,7 @@ namespace SafeExamBrowser.Client
taskbar.InitializeBounds();
logger.Info("Desktop successfully restored.");
if (!displayMonitor.IsAllowedConfiguration(Settings.Display))
if (!displayMonitor.ValidateConfiguration(Settings.Display).IsAllowed)
{
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationTerminateOption) };

View file

@ -100,6 +100,8 @@ namespace SafeExamBrowser.I18n.Contracts
MessageBox_NoButton,
MessageBox_DisplayConfigurationError,
MessageBox_DisplayConfigurationErrorTitle,
MessageBox_DisplayConfigurationInternal,
MessageBox_DisplayConfigurationInternalOrExternal,
MessageBox_NotSupportedConfigurationResource,
MessageBox_NotSupportedConfigurationResourceTitle,
MessageBox_OkButton,

View file

@ -220,11 +220,17 @@
Fehler beim Herunterladen
</Entry>
<Entry key="MessageBox_DisplayConfigurationError">
Die aktive Bildschirm-Konfiguration ist nicht erlaubt! Bitte konsultieren Sie das Applikations-Protokoll für mehr Informationen. SEB wird sich nun beenden...
Die aktive Bildschirm-Konfiguration ist nicht erlaubt! Erlaubt sind %%_ALLOWED_COUNT_%% %%_TYPE_%% Bildschirm(e), erkannt wurden %%_INTERNAL_COUNT_%% interne(r) und %%_EXTERNAL_COUNT_%% externe(r) Bildschirm(e). Bitte konsultieren Sie das Applikations-Protokoll für mehr Informationen. SEB wird sich nun beenden...
</Entry>
<Entry key="MessageBox_DisplayConfigurationErrorTitle">
Verbotene Bildschirm-Konfiguration
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternal">
interne(r)
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternalOrExternal">
interne(r) oder externe(r)
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
Die Konfigurations-Ressource "%%URI%%" enthält ungültige Daten!
</Entry>

View file

@ -220,11 +220,17 @@
Download Error
</Entry>
<Entry key="MessageBox_DisplayConfigurationError">
The active display configuration is not permitted. Please consult the log files for more information. SEB will now shut down...
The active display configuration is not permitted. %%_ALLOWED_COUNT_%% %%_TYPE_%% display(s) are allowed, but %%_INTERNAL_COUNT_%% internal and %%_EXTERNAL_COUNT_%% external display(s) were detected. Please consult the log files for more information. SEB will now shut down...
</Entry>
<Entry key="MessageBox_DisplayConfigurationErrorTitle">
Prohibited Display Configuration
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternal">
internal
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternalOrExternal">
internal or external
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
The configuration resource "%%URI%%" contains invalid data!
</Entry>

View file

@ -220,11 +220,17 @@
Erreur de téléchargement
</Entry>
<Entry key="MessageBox_DisplayConfigurationError">
La configuration d'affichage active n'est pas autorisée. Veuillez consulter les fichiers journaux pour plus d'informations. SEB va maintenant s'arrêter...
La configuration d'affichage active n'est pas autorisée. %%_ALLOWED_COUNT_%% affichages %%_TYPE_%% sont autorisés, mais %%_INTERNAL_COUNT_%% affichages interne(s) et %%_EXTERNAL_COUNT_%% externe(s) ont été détecté(s). Veuillez consulter les fichiers journaux pour plus d'informations. SEB va maintenant s'arrêter...
</Entry>
<Entry key="MessageBox_DisplayConfigurationErrorTitle">
Configuration d'affichage interdite
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternal">
interne(s)
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternalOrExternal">
interne(s) ou externe(s)
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
La ressource de configuration "%%URI%%" contient des données non valides!
</Entry>

View file

@ -220,11 +220,17 @@
Errore di download
</Entry>
<Entry key="MessageBox_DisplayConfigurationError">
La configurazione del display attiva non è consentita. Si prega di consultare i file di registro per ulteriori informazioni. SEB ora si spegnerà...
La configurazione del display attiva non è consentita. Sono consentiti %%_ALLOWED_COUNT_%% display %%_TYPE_%%, ma sono stati rilevati %%_INTERNAL_COUNT_%% display interno/i e %%_EXTERNAL_COUNT_%% esterno/i. Si prega di consultare i file di registro per ulteriori informazioni. SEB ora si spegnerà...
</Entry>
<Entry key="MessageBox_DisplayConfigurationErrorTitle">
Configurazione del display vietata
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternal">
interno/i
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternalOrExternal">
interno/i o esterno/i
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
La risorsa di configurazione "%%URI%%" contiene dati non validi!
</Entry>

View file

@ -190,11 +190,17 @@
下载错误
</Entry>
<Entry key="MessageBox_DisplayConfigurationError">
不允许使用活动的显示配置。 请查阅日志文件以获取更多信息。 SEB现在将关闭...
不允许使用活动的显示配置。 允许使用 %%_ALLOWED_COUNT_%% 个%%_TYPE_%%显示器,但检测到 %%_INTERNAL_COUNT_%% 个内部和 %%_EXTERNAL_COUNT_%% 个外部显示器。 请查阅日志文件以获取更多信息。 SEB现在将关闭...
</Entry>
<Entry key="MessageBox_DisplayConfigurationErrorTitle">
禁止的显示配置
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternal">
内部的
</Entry>
<Entry key="MessageBox_DisplayConfigurationInternalOrExternal">
内部或外部
</Entry>
<Entry key="MessageBox_InvalidConfigurationData">
配置"%%URI%%" 中包含无效数据。
</Entry>
@ -360,6 +366,9 @@
<Entry key="OperationStatus_InitializeRuntimeConnection">
初始化运行时连接
</Entry>
<Entry key="OperationStatus_InitializeServer">
初始化 SEB 服务器
</Entry>
<Entry key="OperationStatus_InitializeServiceSession">
初始化服务会话
</Entry>

View file

@ -26,11 +26,6 @@ namespace SafeExamBrowser.Monitoring.Contracts.Display
/// </summary>
void InitializePrimaryDisplay(int taskbarHeight);
/// <summary>
/// Indicates whether the currently active display configuration is allowed according to the given settings.
/// </summary>
bool IsAllowedConfiguration(DisplaySettings settings);
/// <summary>
/// Prevents the computer from entering sleep mode and turning its display(s) off.
/// </summary>
@ -50,5 +45,10 @@ namespace SafeExamBrowser.Monitoring.Contracts.Display
/// Stops monitoring for display changes.
/// </summary>
void StopMonitoringDisplayChanges();
/// <summary>
/// Validates the currently active display configuration according to the given settings.
/// </summary>
ValidationResult ValidateConfiguration(DisplaySettings settings);
}
}

View file

@ -0,0 +1,31 @@
/*
* Copyright (c) 2021 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.Monitoring.Contracts.Display
{
/// <summary>
/// Provides the result of a display configuration validation.
/// </summary>
public class ValidationResult
{
/// <summary>
/// Specifies the count of external displays detected.
/// </summary>
public int ExternalDisplays { get; set; }
/// <summary>
/// Specifies the count of internal displays detected.
/// </summary>
public int InternalDisplays { get; set; }
/// <summary>
/// Indicates whether the active display configuration is allowed.
/// </summary>
public bool IsAllowed { get; set; }
}
}

View file

@ -60,6 +60,7 @@
<Compile Include="Display\Events\DisplayChangedEventHandler.cs" />
<Compile Include="Applications\Events\ExplorerStartedEventHandler.cs" />
<Compile Include="Display\IDisplayMonitor.cs" />
<Compile Include="Display\ValidationResult.cs" />
<Compile Include="Keyboard\IKeyboardInterceptor.cs" />
<Compile Include="Mouse\IMouseInterceptor.cs" />
<Compile Include="Applications\InitializationResult.cs" />

View file

@ -46,41 +46,6 @@ namespace SafeExamBrowser.Monitoring.Display
InitializeWallpaper();
}
public bool IsAllowedConfiguration(DisplaySettings settings)
{
var allowed = false;
if (TryLoadDisplays(out var displays))
{
var active = displays.Where(d => d.IsActive);
var count = active.Count();
allowed = count <= settings.AllowedDisplays;
if (allowed)
{
logger.Info($"Detected {count} active displays, {settings.AllowedDisplays} are allowed.");
}
else
{
logger.Warn($"Detected {count} active displays but only {settings.AllowedDisplays} are allowed!");
}
if (settings.InternalDisplayOnly && active.Any(d => !d.IsInternal))
{
allowed = false;
logger.Warn("Detected external display but only internal displays are allowed!");
}
}
else
{
allowed = settings.IgnoreError;
logger.Warn($"Failed to validate display configuration, {(allowed ? "ignoring error" : "active configuration is not allowed")}.");
}
return allowed;
}
public void PreventSleepMode()
{
nativeMethods.PreventSleepMode();
@ -105,6 +70,43 @@ namespace SafeExamBrowser.Monitoring.Display
logger.Info("Stopped monitoring display changes.");
}
public ValidationResult ValidateConfiguration(DisplaySettings settings)
{
var result = new ValidationResult();
if (TryLoadDisplays(out var displays))
{
var active = displays.Where(d => d.IsActive);
var count = active.Count();
result.ExternalDisplays = active.Count(d => !d.IsInternal);
result.InternalDisplays = active.Count(d => d.IsInternal);
result.IsAllowed = count <= settings.AllowedDisplays;
if (result.IsAllowed)
{
logger.Info($"Detected {count} active displays, {settings.AllowedDisplays} are allowed.");
}
else
{
logger.Warn($"Detected {count} active displays but only {settings.AllowedDisplays} are allowed!");
}
if (settings.InternalDisplayOnly && active.Any(d => !d.IsInternal))
{
result.IsAllowed = false;
logger.Warn("Detected external display but only internal displays are allowed!");
}
}
else
{
result.IsAllowed = settings.IgnoreError;
logger.Warn($"Failed to validate display configuration, {(result.IsAllowed ? "ignoring error" : "active configuration is not allowed")}.");
}
return result;
}
private void SystemEvents_DisplaySettingsChanged(object sender, EventArgs e)
{
logger.Info("Display change detected!");

View file

@ -91,7 +91,7 @@ namespace SafeExamBrowser.Runtime
sessionOperations.Enqueue(new DisclaimerOperation(logger, sessionContext));
sessionOperations.Enqueue(new RemoteSessionOperation(remoteSessionDetector, logger, sessionContext));
sessionOperations.Enqueue(new VirtualMachineOperation(vmDetector, logger, sessionContext));
sessionOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, sessionContext));
sessionOperations.Enqueue(new DisplayMonitorOperation(displayMonitor, logger, sessionContext, text));
sessionOperations.Enqueue(new ServiceOperation(logger, runtimeHost, serviceProxy, sessionContext, THIRTY_SECONDS, userInfo));
sessionOperations.Enqueue(new ClientTerminationOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
sessionOperations.Enqueue(new ProctoringWorkaroundOperation(logger, sessionContext));

View file

@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
@ -20,14 +21,16 @@ namespace SafeExamBrowser.Runtime.Operations
{
private readonly IDisplayMonitor displayMonitor;
private readonly ILogger logger;
private readonly IText text;
public override event ActionRequiredEventHandler ActionRequired;
public override event StatusChangedEventHandler StatusChanged;
public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, SessionContext context) : base(context)
public DisplayMonitorOperation(IDisplayMonitor displayMonitor, ILogger logger, SessionContext context, IText text) : base(context)
{
this.displayMonitor = displayMonitor;
this.logger = logger;
this.text = text;
}
public override OperationResult Perform()
@ -47,26 +50,34 @@ namespace SafeExamBrowser.Runtime.Operations
private OperationResult CheckDisplayConfiguration()
{
var args = new MessageEventArgs
{
Action = MessageBoxAction.Ok,
Icon = MessageBoxIcon.Error,
Message = TextKey.MessageBox_DisplayConfigurationError,
Title = TextKey.MessageBox_DisplayConfigurationErrorTitle
};
var result = OperationResult.Aborted;
logger.Info("Validating display configuration...");
StatusChanged?.Invoke(TextKey.OperationStatus_ValidateDisplayConfiguration);
if (displayMonitor.IsAllowedConfiguration(Context.Next.Settings.Display))
var result = OperationResult.Failed;
var validation = displayMonitor.ValidateConfiguration(Context.Next.Settings.Display);
if (validation.IsAllowed)
{
logger.Info("Display configuration is allowed.");
result = OperationResult.Success;
}
else
{
var args = new MessageEventArgs
{
Action = MessageBoxAction.Ok,
Icon = MessageBoxIcon.Error,
Message = TextKey.MessageBox_DisplayConfigurationError,
Title = TextKey.MessageBox_DisplayConfigurationErrorTitle
};
logger.Error("Display configuration is not allowed!");
args.MessagePlaceholders.Add("%%_ALLOWED_COUNT_%%", Convert.ToString(Context.Next.Settings.Display.AllowedDisplays));
args.MessagePlaceholders.Add("%%_TYPE_%%", Context.Next.Settings.Display.InternalDisplayOnly ? text.Get(TextKey.MessageBox_DisplayConfigurationInternal) : text.Get(TextKey.MessageBox_DisplayConfigurationInternalOrExternal));
args.MessagePlaceholders.Add("%%_EXTERNAL_COUNT_%%", Convert.ToString(validation.ExternalDisplays));
args.MessagePlaceholders.Add("%%_INTERNAL_COUNT_%%", Convert.ToString(validation.InternalDisplays));
ActionRequired?.Invoke(args);
}