diff --git a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs index ccbec1db..b5fce4d6 100644 --- a/SafeExamBrowser.Runtime/Operations/ServerOperation.cs +++ b/SafeExamBrowser.Runtime/Operations/ServerOperation.cs @@ -51,6 +51,10 @@ namespace SafeExamBrowser.Runtime.Operations if (Context.Next.Settings.SessionMode == SessionMode.Server) { + var exam = default(Exam); + var exams = default(IEnumerable); + var uri = default(Uri); + logger.Info("Initializing server..."); StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServer); @@ -60,22 +64,27 @@ namespace SafeExamBrowser.Runtime.Operations if (success) { - (abort, fallback, success) = TryPerformWithFallback(() => server.GetAvailableExams(Context.Next.Settings.Server.ExamId), out var exams); + (abort, fallback, success) = TryPerformWithFallback(() => server.GetAvailableExams(Context.Next.Settings.Server.ExamId), out exams); + } - if (success) - { - success = TrySelectExam(exams, out var exam); + if (success) + { + success = TrySelectExam(exams, out exam); + } - if (success) - { - (abort, fallback, success) = TryPerformWithFallback(() => server.GetConfigurationFor(exam), out var uri); + if (success) + { + (abort, fallback, success) = TryPerformWithFallback(() => server.GetConfigurationFor(exam), out uri); + } - if (success) - { - result = TryLoadServerSettings(exam, uri); - } - } - } + if (success) + { + result = TryLoadServerSettings(exam, uri); + } + + if (success && result == OperationResult.Success) + { + (abort, fallback, success) = TryPerformWithFallback(() => server.SendSelectedExam(exam)); } if (abort) @@ -97,25 +106,22 @@ namespace SafeExamBrowser.Runtime.Operations public override OperationResult Repeat() { - if (Context.Current.Settings.SessionMode == SessionMode.Server) - { - if (Context.Next.Settings.SessionMode == SessionMode.Server) - { - ShowReconfigurationError(); + var result = OperationResult.Success; - return OperationResult.Aborted; - } - else - { - return Revert(); - } + if (Context.Current.Settings.SessionMode == SessionMode.Server && Context.Next.Settings.SessionMode == SessionMode.Server) + { + result = AbortServerReconfiguration(); + } + else if (Context.Current.Settings.SessionMode == SessionMode.Server) + { + result = Revert(); } else if (Context.Next.Settings.SessionMode == SessionMode.Server) { - return Perform(); + result = Perform(); } - return OperationResult.Success; + return result; } public override OperationResult Revert() @@ -254,7 +260,7 @@ namespace SafeExamBrowser.Runtime.Operations return success; } - private void ShowReconfigurationError() + private OperationResult AbortServerReconfiguration() { var args = new MessageEventArgs { @@ -263,9 +269,11 @@ namespace SafeExamBrowser.Runtime.Operations Message = TextKey.MessageBox_ServerReconfigurationWarning, Title = TextKey.MessageBox_ServerReconfigurationWarningTitle }; - logger.Warn("Server reconfiguration requested but is not allowed."); + logger.Warn("Server reconfiguration is currently not supported, aborting..."); ActionRequired?.Invoke(args); + + return OperationResult.Aborted; } } } diff --git a/SafeExamBrowser.Server.Contracts/IServerProxy.cs b/SafeExamBrowser.Server.Contracts/IServerProxy.cs index 4f320610..c4959337 100644 --- a/SafeExamBrowser.Server.Contracts/IServerProxy.cs +++ b/SafeExamBrowser.Server.Contracts/IServerProxy.cs @@ -99,6 +99,11 @@ namespace SafeExamBrowser.Server.Contracts /// ServerResponse LowerHand(); + /// + /// Sends the selected exam to the server. + /// + ServerResponse SendSelectedExam(Exam exam); + /// /// Sends the given user session identifier of a LMS and thus establishes a connection with the server. /// diff --git a/SafeExamBrowser.Server/Parser.cs b/SafeExamBrowser.Server/Parser.cs index 51b430fa..67a6d7f4 100644 --- a/SafeExamBrowser.Server/Parser.cs +++ b/SafeExamBrowser.Server/Parser.cs @@ -105,6 +105,27 @@ namespace SafeExamBrowser.Server return success; } + internal bool TryParseAppSignatureKeySalt(HttpResponseMessage response, out string salt) + { + salt = default; + + try + { + var hasHeader = response.Headers.TryGetValues("SEBExamSalt", out var values); + + if (hasHeader) + { + salt = values.First(); + } + } + catch (Exception e) + { + logger.Error("Failed to parse app signature key salt!", e); + } + + return salt != default; + } + internal bool TryParseConnectionToken(HttpResponseMessage response, out string connectionToken) { connectionToken = default; diff --git a/SafeExamBrowser.Server/ServerProxy.cs b/SafeExamBrowser.Server/ServerProxy.cs index abf49e3c..43921a77 100644 --- a/SafeExamBrowser.Server/ServerProxy.cs +++ b/SafeExamBrowser.Server/ServerProxy.cs @@ -348,6 +348,38 @@ namespace SafeExamBrowser.Server return new ServerResponse(success, response.ToLogString()); } + public ServerResponse SendSelectedExam(Exam exam) + { + var authorization = ("Authorization", $"Bearer {oauth2Token}"); + var content = $"examId={exam.Id}"; + var contentType = "application/x-www-form-urlencoded"; + var token = ("SEBConnectionToken", connectionToken); + + var success = TryExecute(new HttpMethod("PATCH"), api.HandshakeEndpoint, out var response, content, contentType, authorization, token); + var message = response.ToLogString(); + + if (success) + { + logger.Info("Successfully sent selected exam."); + } + else + { + logger.Error("Failed to send selected exam!"); + } + + if (parser.TryParseAppSignatureKeySalt(response, out var salt)) + { + logger.Info("App signature key salt detected, performing key exchange..."); + success = TrySendAppSignatureKey(out message); + } + else + { + logger.Info("No app signature key salt detected, skipping key exchange."); + } + + return new ServerResponse(success, message); + } + public ServerResponse SendSessionIdentifier(string identifier) { var authorization = ("Authorization", $"Bearer {oauth2Token}"); @@ -614,6 +646,31 @@ namespace SafeExamBrowser.Server return success; } + private bool TrySendAppSignatureKey(out string message) + { + // TODO: + // keyGenerator.CalculateAppSignatureKey(configurationKey, server.AppSignatureKeySalt) + + var authorization = ("Authorization", $"Bearer {oauth2Token}"); + var content = $"seb_signature_key={"WINDOWS-TEST-ASK-1234"}"; + var contentType = "application/x-www-form-urlencoded"; + var token = ("SEBConnectionToken", connectionToken); + var success = TryExecute(new HttpMethod("PATCH"), api.HandshakeEndpoint, out var response, content, contentType, authorization, token); + + message = response.ToLogString(); + + if (success) + { + logger.Info("Successfully sent app signature key."); + } + else + { + logger.Error("Failed to send app signature key!"); + } + + return success; + } + private bool TryExecute( HttpMethod method, string url,