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

@@ -0,0 +1,316 @@
/*
* 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 System;
using System.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Configuration.Contracts;
using SafeExamBrowser.Core.Contracts.OperationModel;
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
using SafeExamBrowser.I18n.Contracts;
using SafeExamBrowser.Server.Contracts;
using SafeExamBrowser.Server.Contracts.Data;
using SafeExamBrowser.Settings;
using SafeExamBrowser.SystemComponents.Contracts;
using SafeExamBrowser.UserInterface.Contracts;
namespace SafeExamBrowser.Runtime.Operations.Session
{
internal class ServerOperation : ConfigurationBaseOperation
{
private readonly IFileSystem fileSystem;
private readonly IServerProxy server;
public override event StatusChangedEventHandler StatusChanged;
public ServerOperation(
Dependencies dependencies,
IFileSystem fileSystem,
IConfigurationRepository repository,
IServerProxy server,
IUserInterfaceFactory uiFactory) : base(dependencies, repository, uiFactory)
{
this.fileSystem = fileSystem;
this.server = server;
}
public override OperationResult Perform()
{
var result = OperationResult.Success;
if (Context.Next.Settings.SessionMode == SessionMode.Server)
{
var browserExamKey = default(string);
var exam = default(Exam);
var exams = default(IEnumerable<Exam>);
var uri = default(Uri);
Logger.Info("Initializing server...");
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer);
server.Initialize(Context.Next.Settings.Server);
var (abort, fallback, success) = TryPerformWithFallback(() => server.Connect());
if (success)
{
(abort, fallback, success) = TryPerformWithFallback(() => server.GetAvailableExams(Context.Next.Settings.Server.ExamId), out exams);
}
if (success)
{
success = TrySelectExam(exams, out exam);
}
if (success)
{
(abort, fallback, success) = TryPerformWithFallback(() => server.GetConfigurationFor(exam), out uri);
}
if (success)
{
result = TryLoadServerSettings(exam, uri);
}
if (success && result == OperationResult.Success)
{
(abort, fallback, success) = TryPerformWithFallback(() => server.SendSelectedExam(exam), out browserExamKey);
}
if (browserExamKey != default)
{
Context.Next.Settings.Browser.CustomBrowserExamKey = browserExamKey;
}
if (abort)
{
result = OperationResult.Aborted;
Logger.Info("The user aborted the server operation.");
}
if (fallback)
{
Context.Next.Settings.SessionMode = SessionMode.Normal;
result = OperationResult.Success;
Logger.Info("The user chose to fallback and start a normal session.");
}
if (result == OperationResult.Success)
{
Logger.Info("Successfully initialized server.");
}
else if (result == OperationResult.Failed)
{
Logger.Error("Failed to initialize server!");
}
}
return result;
}
public override OperationResult Repeat()
{
var result = OperationResult.Success;
if (Context.Current.Settings.SessionMode == SessionMode.Server && Context.Next.Settings.SessionMode == SessionMode.Server)
{
result = Revert();
if (result == OperationResult.Success)
{
result = Perform();
}
else
{
Logger.Error($"Cannot start new server session due to failed finalization of current server session! Terminating...");
}
}
else if (Context.Current.Settings.SessionMode == SessionMode.Server)
{
result = Revert();
}
else if (Context.Next.Settings.SessionMode == SessionMode.Server)
{
result = Perform();
}
return result;
}
public override OperationResult Revert()
{
var result = OperationResult.Success;
if (Context.Current?.Settings.SessionMode == SessionMode.Server || Context.Next?.Settings.SessionMode == SessionMode.Server)
{
Logger.Info("Finalizing server...");
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServer);
var disconnect = server.Disconnect();
if (disconnect.Success)
{
result = OperationResult.Success;
Logger.Info("Successfully finalized server.");
}
else
{
result = OperationResult.Failed;
Logger.Error("Failed to finalize server!");
}
}
return result;
}
private OperationResult TryLoadServerSettings(Exam exam, Uri uri)
{
var info = server.GetConnectionInfo();
var result = OperationResult.Failed;
var status = TryLoadSettings(uri, UriSource.Server, out _, out var settings);
fileSystem.Delete(uri.LocalPath);
if (status == LoadStatus.Success)
{
var browserSettings = Context.Next.Settings.Browser;
var invigilationSettings = settings.Server.Invigilation;
var serverSettings = Context.Next.Settings.Server;
Context.Next.AppConfig.ServerApi = info.Api;
Context.Next.AppConfig.ServerConnectionToken = info.ConnectionToken;
Context.Next.AppConfig.ServerExamId = exam.Id;
Context.Next.AppConfig.ServerOauth2Token = info.Oauth2Token;
Context.Next.Settings = settings;
Context.Next.Settings.Browser.StartUrl = exam.Url;
Context.Next.Settings.Browser.StartUrlQuery = browserSettings.StartUrlQuery;
Context.Next.Settings.Server = serverSettings;
Context.Next.Settings.Server.Invigilation = invigilationSettings;
Context.Next.Settings.SessionMode = SessionMode.Server;
result = OperationResult.Success;
}
return result;
}
private (bool abort, bool fallback, bool success) TryPerformWithFallback(Func<ServerResponse> request)
{
var abort = false;
var fallback = false;
var success = false;
while (!success)
{
var response = request();
success = response.Success;
if (!success && !Retry(response.Message, out abort, out fallback))
{
break;
}
}
return (abort, fallback, success);
}
private (bool abort, bool fallback, bool success) TryPerformWithFallback<T>(Func<ServerResponse<T>> request, out T value)
{
var abort = false;
var fallback = false;
var success = false;
value = default;
while (!success)
{
var response = request();
success = response.Success;
value = response.Value;
if (!success && !Retry(response.Message, out abort, out fallback))
{
break;
}
}
return (abort, fallback, success);
}
private bool Retry(string message, out bool abort, out bool fallback)
{
AskForServerFailureAction(message, out abort, out fallback, out var retry);
if (retry)
{
Logger.Debug("The user chose to retry the current server request.");
}
return retry;
}
private void AskForServerFailureAction(string message, out bool abort, out bool fallback, out bool retry)
{
var showFallback = Context.Next.Settings.Server.PerformFallback;
if (ClientBridge.IsRequired())
{
ClientBridge.TryAskForServerFailureAction(message, showFallback, out abort, out fallback, out retry);
}
else
{
var dialog = uiFactory.CreateServerFailureDialog(message, showFallback);
var result = dialog.Show(RuntimeWindow);
abort = result.Abort;
fallback = result.Fallback;
retry = result.Retry;
}
}
private bool TrySelectExam(IEnumerable<Exam> exams, out Exam exam)
{
var success = true;
if (string.IsNullOrWhiteSpace(Context.Next.Settings.Server.ExamId))
{
success = TryAskForExamSelection(exams, out exam);
}
else
{
exam = exams.First();
Logger.Info("Automatically selected exam as defined in configuration.");
}
return success;
}
private bool TryAskForExamSelection(IEnumerable<Exam> exams, out Exam exam)
{
var success = false;
if (ClientBridge.IsRequired())
{
success = ClientBridge.TryAskForExamSelection(exams, out exam);
}
else
{
var dialog = uiFactory.CreateExamSelectionDialog(exams);
var result = dialog.Show(RuntimeWindow);
exam = result.SelectedExam;
success = result.Success;
}
return success;
}
}
}