Update Safe Exam Browser Patch to 3.10.0.826

This commit is contained in:
2025-09-16 16:32:31 +02:00
parent 4827ae1afc
commit dd82d45ed8
320 changed files with 8445 additions and 5295 deletions

View File

@@ -15,7 +15,7 @@
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-8.3.0.0" newVersion="8.3.0.0" />
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" />

View File

@@ -82,6 +82,11 @@ namespace SafeExamBrowser.Client
/// </summary>
internal IProctoringController Proctoring { get; set; }
/// <summary>
/// Indicates that the user already provided the correct quit password.
/// </summary>
internal bool QuitPasswordValidated { get; set; }
/// <summary>
/// The client responsibilities.
/// </summary>

View File

@@ -58,6 +58,7 @@ using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Shell;
using SafeExamBrowser.UserInterface.Contracts.Windows;
using SafeExamBrowser.UserInterface.Shared;
using SafeExamBrowser.UserInterface.Shared.Activators;
using SafeExamBrowser.WindowsApi;
using SafeExamBrowser.WindowsApi.Contracts;
@@ -92,6 +93,7 @@ namespace SafeExamBrowser.Client
private ITaskview taskview;
private IUserInfo userInfo;
private IText text;
private WindowGuard windowGuard;
private IUserInterfaceFactory uiFactory;
internal ClientController ClientController { get; private set; }
@@ -105,6 +107,7 @@ namespace SafeExamBrowser.Client
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
windowGuard = new WindowGuard(ModuleLogger(nameof(WindowGuard)));
uiFactory = BuildUserInterfaceFactory();
actionCenter = uiFactory.CreateActionCenter();
context = new ClientContext();
@@ -162,6 +165,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new I18nOperation(logger, text));
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
operations.Enqueue(new WindowGuardOperation(context, logger, windowGuard));
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
operations.Enqueue(new DelegateOperation(BuildPowerSupply));
@@ -169,6 +173,7 @@ namespace SafeExamBrowser.Client
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
operations.Enqueue(new PermissionOperation(context, logger, networkAdapter));
operations.Enqueue(new ApplicationOperation(context, applicationFactory, fileSystemDialog, logger, messageBox, applicationMonitor, splashScreen, text));
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
@@ -196,7 +201,7 @@ namespace SafeExamBrowser.Client
responsibilities.Enqueue(new IntegrityResponsibility(context, ModuleLogger(nameof(IntegrityResponsibility)), text));
responsibilities.Enqueue(new MonitoringResponsibility(actionCenter, applicationMonitor, context, coordinator, displayMonitor, explorerShell, ModuleLogger(nameof(MonitoringResponsibility)), sentinel, taskbar, text));
responsibilities.Enqueue(new NetworkResponsibility(context, ModuleLogger(nameof(NetworkResponsibility)), networkAdapter, text, uiFactory));
responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), uiFactory));
responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), messageBox, uiFactory));
responsibilities.Enqueue(new ServerResponsibility(context, coordinator, ModuleLogger(nameof(ServerResponsibility)), text));
responsibilities.Enqueue(new ShellResponsibility(actionCenter, context, new HashAlgorithm(), ModuleLogger(nameof(ShellResponsibility)), messageBox, taskbar, uiFactory));
@@ -337,7 +342,8 @@ namespace SafeExamBrowser.Client
{
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)));
var server = new ServerProxy(context.AppConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter);
var operation = new ServerOperation(context, logger, server);
var invigilator = new Invigilator(ModuleLogger(nameof(Invigilator)), server);
var operation = new ServerOperation(actionCenter, context, invigilator, logger, server, taskbar, uiFactory);
context.Server = server;
@@ -381,9 +387,9 @@ namespace SafeExamBrowser.Client
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new Mobile.FileSystemDialogFactory(systemInfo, text);
return new Mobile.FileSystemDialogFactory(systemInfo, text, windowGuard);
default:
return new Desktop.FileSystemDialogFactory(systemInfo, text);
return new Desktop.FileSystemDialogFactory(systemInfo, text, windowGuard);
}
}
@@ -403,9 +409,9 @@ namespace SafeExamBrowser.Client
switch (uiMode)
{
case UserInterfaceMode.Mobile:
return new Mobile.UserInterfaceFactory(text);
return new Mobile.UserInterfaceFactory(text, windowGuard);
default:
return new Desktop.UserInterfaceFactory(text);
return new Desktop.UserInterfaceFactory(text, windowGuard);
}
}

View File

@@ -16,12 +16,7 @@ namespace SafeExamBrowser.Client.Operations
/// </summary>
internal abstract class ClientOperation : IOperation
{
protected ClientContext Context { get; private set; }
/// <summary>
/// TODO: In case this event is neither used by the runtime, either remove it completely or then move it to a separate interface!
/// </summary>
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
protected ClientContext Context { get; }
public abstract event StatusChangedEventHandler StatusChanged;

View File

@@ -0,0 +1,60 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.SystemComponents.Contracts.Network;
namespace SafeExamBrowser.Client.Operations
{
internal class PermissionOperation : ClientOperation
{
private readonly ILogger logger;
private readonly INetworkAdapter networkAdapter;
public override event StatusChangedEventHandler StatusChanged;
public PermissionOperation(ClientContext context, ILogger logger, INetworkAdapter networkAdapter) : base(context)
{
this.logger = logger;
this.networkAdapter = networkAdapter;
}
public override OperationResult Perform()
{
logger.Info("Initializing permissions...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializePermissions);
RequestNetworkAdapterAccess();
return OperationResult.Success;
}
public override OperationResult Revert()
{
return OperationResult.Success;
}
private void RequestNetworkAdapterAccess()
{
var granted = networkAdapter.RequestAccess();
if (granted)
{
logger.Info("Permission to access the wireless networking functionality has been granted.");
}
else
{
logger.Warn("Permission to access the wireless networking functionality has not been granted! " +
"If required, please grant the location permission manually under 'Privacy & Security' in the system settings.");
}
}
}
}

View File

@@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
@@ -51,12 +50,6 @@ namespace SafeExamBrowser.Client.Operations
controller.Initialize(Context.Settings.Proctoring);
if (Context.Settings.SessionMode == SessionMode.Server && Context.Settings.Proctoring.ShowRaiseHandNotification)
{
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.ActionCenter, Context.Settings.Proctoring));
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.Taskbar, Context.Settings.Proctoring));
}
foreach (var notification in controller.Notifications)
{
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter));

View File

@@ -12,20 +12,37 @@ using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Settings;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.Shell;
namespace SafeExamBrowser.Client.Operations
{
internal class ServerOperation : ClientOperation
{
private readonly IActionCenter actionCenter;
private readonly IInvigilator invigilator;
private readonly ILogger logger;
private readonly IServerProxy server;
private readonly ITaskbar taskbar;
private readonly IUserInterfaceFactory uiFactory;
public override event StatusChangedEventHandler StatusChanged;
public ServerOperation(ClientContext context, ILogger logger, IServerProxy server) : base(context)
public ServerOperation(
IActionCenter actionCenter,
ClientContext context,
IInvigilator invigilator,
ILogger logger,
IServerProxy server,
ITaskbar taskbar,
IUserInterfaceFactory uiFactory) : base(context)
{
this.actionCenter = actionCenter;
this.invigilator = invigilator;
this.logger = logger;
this.server = server;
this.taskbar = taskbar;
this.uiFactory = uiFactory;
}
public override OperationResult Perform()
@@ -42,6 +59,14 @@ namespace SafeExamBrowser.Client.Operations
Context.AppConfig.ServerOauth2Token,
Context.Settings.Server);
server.StartConnectivity();
if (Context.Settings.Server.Invigilation.ShowRaiseHandNotification)
{
invigilator.Initialize(Context.Settings.Server.Invigilation);
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.ActionCenter, Context.Settings.Server.Invigilation));
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.Taskbar, Context.Settings.Server.Invigilation));
}
}
return OperationResult.Success;

View File

@@ -83,7 +83,7 @@ namespace SafeExamBrowser.Client.Operations
InitializeSystemComponents();
InitializeActionCenter();
InitializeTaskbar();
InitializeTaskview();
//InitializeTaskview();
InitializeActivators();
InitializeAlwaysOnState();
@@ -112,11 +112,11 @@ namespace SafeExamBrowser.Client.Operations
actionCenterActivator.Start();
}
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
{
taskview.Register(taskViewActivator);
taskViewActivator.Start();
}
//if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
//{
// taskview.Register(taskViewActivator);
// taskViewActivator.Start();
//}
if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator)
{

View File

@@ -0,0 +1,54 @@
/*
* Copyright (c) 2025 ETH Zürich, IT Services
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
namespace SafeExamBrowser.Client.Operations
{
internal class WindowGuardOperation : ClientOperation
{
private readonly ILogger logger;
private readonly IWindowGuard guard;
public WindowGuardOperation(ClientContext context, ILogger logger, IWindowGuard guard) : base(context)
{
this.logger = logger;
this.guard = guard;
}
public override event StatusChangedEventHandler StatusChanged;
public override OperationResult Perform()
{
logger.Info("Initializing window guard...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWindowGuard);
if (Context.Settings.Proctoring.Enabled || Context.Settings.Security.AllowWindowCapture)
{
guard.Deactivate();
logger.Info($"Deactivated window guard because {(Context.Settings.Proctoring.Enabled ? "proctoring" : "window capturing")} is enabled.");
}
else
{
guard.Activate();
logger.Info("Activated window guard.");
}
return OperationResult.Success;
}
public override OperationResult Revert()
{
return OperationResult.Success;
}
}
}

View File

@@ -51,6 +51,6 @@ using System.Windows;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("3.9.0.787")]
[assembly: AssemblyFileVersion("3.9.0.787")]
[assembly: AssemblyVersion("3.10.0.826")]
[assembly: AssemblyFileVersion("3.10.0.826")]
[assembly: AssemblyInformationalVersion("1.0.0.0")]

View File

@@ -40,6 +40,20 @@ namespace SafeExamBrowser.Client.Responsibilities
}
}
protected bool IsValidQuitPassword(string password)
{
var actual = Context.HashAlgorithm.GenerateHashFor(password);
var expected = Settings.Security.QuitPasswordHash;
var valid = expected.Equals(actual, StringComparison.OrdinalIgnoreCase);
if (valid)
{
Context.QuitPasswordValidated = true;
}
return valid;
}
protected void PrepareShutdown()
{
Context.Responsibilities.Delegate(ClientTask.PrepareShutdown_Wave1);
@@ -56,72 +70,30 @@ namespace SafeExamBrowser.Client.Responsibilities
protected LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var result = default(LockScreenResult);
Context.LockScreen = Context.UserInterfaceFactory.CreateLockScreen(message, title, options, Settings.UserInterface.LockScreen);
Logger.Info("Showing lock screen...");
PauseActivators();
Context.LockScreen = Context.UserInterfaceFactory.CreateLockScreen(message, title, options, Settings.UserInterface.LockScreen);
Context.LockScreen.Show();
if (Settings.SessionMode == SessionMode.Server)
{
var response = Context.Server.LockScreen(message);
if (!response.Success)
{
Logger.Error($"Failed to send lock screen notification to server! Message: {response.Message}.");
}
SendLockScreenNotification(message);
}
for (var unlocked = false; !unlocked;)
{
result = Context.LockScreen.WaitForResult();
if (result.Canceled)
{
Logger.Info("The lock screen has been automaticaly canceled.");
unlocked = true;
}
else if (hasQuitPassword)
{
var passwordHash = Context.HashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
Logger.Info("The user entered the correct unlock password.");
unlocked = true;
}
else
{
Logger.Info("The user entered the wrong unlock password.");
Context.MessageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: Context.LockScreen);
}
}
else
{
Logger.Warn($"No unlock password is defined, allowing user to resume session!");
unlocked = true;
}
}
var result = WaitForLockScreenResolution();
Context.LockScreen.Close();
Context.LockScreen = default;
ResumeActivators();
Logger.Info("Closed lock screen.");
if (Settings.SessionMode == SessionMode.Server)
{
var response = Context.Server.ConfirmLockScreen();
if (!response.Success)
{
Logger.Error($"Failed to send lock screen confirm notification to server! Message: {response.Message}.");
}
SendLockScreenConfirmation();
}
Logger.Info("Closed lock screen.");
return result;
}
@@ -139,5 +111,65 @@ namespace SafeExamBrowser.Client.Responsibilities
return communication.Success;
}
private void SendLockScreenConfirmation()
{
var response = Context.Server.ConfirmLockScreen();
if (!response.Success)
{
Logger.Error($"Failed to send lock screen confirmation to server! Message: {response.Message}.");
}
}
private void SendLockScreenNotification(string message)
{
var response = Context.Server.LockScreen(message);
if (!response.Success)
{
Logger.Error($"Failed to send lock screen notification to server! Message: {response.Message}.");
}
}
private LockScreenResult WaitForLockScreenResolution()
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var result = default(LockScreenResult);
for (var unlocked = false; !unlocked;)
{
result = Context.LockScreen.WaitForResult();
if (result.Canceled)
{
Logger.Info("The lock screen has been canceled automatically.");
unlocked = true;
}
else if (hasQuitPassword)
{
var passwordHash = Context.HashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
Logger.Info("The user entered the correct unlock password.");
unlocked = true;
}
else
{
Logger.Info("The user entered a wrong unlock password.");
Context.MessageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: Context.LockScreen);
}
}
else
{
Logger.Warn($"No unlock password is defined, allowing user to resume session!");
unlocked = true;
}
}
return result;
}
}
}

View File

@@ -9,6 +9,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Win32;
using SafeExamBrowser.Client.Contracts;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
@@ -251,23 +252,29 @@ namespace SafeExamBrowser.Client.Responsibilities
}
}
private void Sentinel_SessionChanged()
private void Sentinel_SessionChanged(SessionSwitchReason reason)
{
var allow = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
var disable = Settings.Security.DisableSessionChangeLockScreen;
if (allow || disable)
var allowed = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
var disabled = Settings.Security.DisableSessionChangeLockScreen;
var ignore = Settings.Service.IgnoreService && (reason == SessionSwitchReason.SessionLock || reason == SessionSwitchReason.SessionUnlock);
if (allowed || disabled)
{
Logger.Info($"Detected user session change, but {(allow ? "session locking and/or switching is allowed" : "lock screen is deactivated")}.");
Logger.Info($"Detected user session change ({reason}), but {(allowed ? "session locking and/or switching is allowed" : "lock screen is disabled")}.");
}
else if (ignore)
{
Logger.Info($"Ignoring user session change ({reason}).");
}
else
{
var message = text.Get(TextKey.LockScreen_UserSessionMessage);
var message = text.Get(Settings.Service.IgnoreService ? TextKey.LockScreen_UserSwitchMessage : TextKey.LockScreen_UserSessionMessage);
var title = text.Get(TextKey.LockScreen_Title);
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionContinueOption) };
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionTerminateOption) };
Logger.Warn("User session changed! Attempting to show lock screen...");
Logger.Warn($"User session changed ({reason})! Attempting to show lock screen...");
if (coordinator.RequestSessionLock())
{

View File

@@ -7,21 +7,33 @@
*/
using System.Threading.Tasks;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Logging.Contracts;
using SafeExamBrowser.Proctoring.Contracts;
using SafeExamBrowser.Proctoring.Contracts.Events;
using SafeExamBrowser.UserInterface.Contracts;
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
namespace SafeExamBrowser.Client.Responsibilities
{
internal class ProctoringResponsibility : ClientResponsibility
{
private readonly IMessageBox messageBox;
private readonly IUserInterfaceFactory uiFactory;
private bool cancel;
private IProctoringController Proctoring => Context.Proctoring;
public ProctoringResponsibility(ClientContext context, ILogger logger, IUserInterfaceFactory uiFactory) : base(context, logger)
public ProctoringResponsibility(
ClientContext context,
ILogger logger,
IMessageBox messageBox,
IUserInterfaceFactory uiFactory) : base(context, logger)
{
this.messageBox = messageBox;
this.uiFactory = uiFactory;
}
@@ -37,8 +49,10 @@ namespace SafeExamBrowser.Client.Responsibilities
{
if (Proctoring != default && Proctoring.HasRemainingWork())
{
var dialog = uiFactory.CreateProctoringFinalizationDialog();
var handler = new RemainingWorkUpdatedEventHandler((args) => dialog.Update(args));
var dialog = uiFactory.CreateProctoringFinalizationDialog(!Context.QuitPasswordValidated);
var handler = new RemainingWorkUpdatedEventHandler((args) => Proctoring_RemainingWorkUpdated(dialog, args));
dialog.CancellationRequested += new CancellationRequestedEventHandler(() => Dialog_CancellationRequested(dialog));
Task.Run(() =>
{
@@ -50,5 +64,28 @@ namespace SafeExamBrowser.Client.Responsibilities
dialog.Show();
}
}
private void Dialog_CancellationRequested(IProctoringFinalizationDialog dialog)
{
var alreadyValidated = Context.QuitPasswordValidated;
if (alreadyValidated || IsValidQuitPassword(dialog.QuitPassword))
{
cancel = true;
Logger.Info($"The user {(alreadyValidated ? "already " : "")}entered the correct quit password, cancelling remaining work...");
}
else
{
cancel = false;
Logger.Info("The user entered the wrong quit password, remaining work will continue.");
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning, parent: dialog);
}
}
private void Proctoring_RemainingWorkUpdated(IProctoringFinalizationDialog dialog, RemainingWorkUpdatedEventArgs args)
{
dialog.Update(args);
args.CancellationRequested = cancel;
}
}
}

View File

@@ -6,7 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.ComponentModel;
using System.Linq;
using SafeExamBrowser.Configuration.Contracts.Cryptography;
@@ -134,14 +133,14 @@ namespace SafeExamBrowser.Client.Responsibilities
{
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
var initiateShutdown = hasQuitPassword ? TryValidateQuitPassword() : TryConfirmShutdown();
var succes = false;
var success = false;
if (initiateShutdown)
{
succes = TryRequestShutdown();
success = TryRequestShutdown();
}
return succes;
return success;
}
private bool TryConfirmShutdown()
@@ -161,26 +160,20 @@ namespace SafeExamBrowser.Client.Responsibilities
{
var dialog = uiFactory.CreatePasswordDialog(TextKey.PasswordDialog_QuitPasswordRequired, TextKey.PasswordDialog_QuitPasswordRequiredTitle);
var result = dialog.Show();
var success = false;
if (result.Success)
if (result.Success && IsValidQuitPassword(result.Password))
{
var passwordHash = hashAlgorithm.GenerateHashFor(result.Password);
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
if (isCorrect)
{
Logger.Info("The user entered the correct quit password, the application will now terminate.");
}
else
{
Logger.Info("The user entered the wrong quit password.");
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning);
}
return isCorrect;
success = true;
Logger.Info("The user entered the correct quit password, the application will now terminate.");
}
else if (result.Success)
{
Logger.Info("The user entered the wrong quit password.");
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning);
}
return false;
return success;
}
}
}

View File

@@ -80,6 +80,7 @@
<Compile Include="Operations\ClientOperation.cs" />
<Compile Include="Operations\ClientOperationSequence.cs" />
<Compile Include="Operations\ConfigurationOperation.cs" />
<Compile Include="Operations\PermissionOperation.cs" />
<Compile Include="Operations\ProctoringOperation.cs" />
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
<Compile Include="Communication\ClientHost.cs" />
@@ -94,6 +95,7 @@
<Compile Include="Operations\ApplicationOperation.cs" />
<Compile Include="Operations\ServerOperation.cs" />
<Compile Include="Operations\ShellOperation.cs" />
<Compile Include="Operations\WindowGuardOperation.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>