SEBWIN-762: Added user identifier detection via Moodle plugin and overall renamed session to user identifier.

This commit is contained in:
Damian Büchel 2023-11-01 13:52:39 +01:00
parent 8c3d9a31d7
commit 751bfcb144
13 changed files with 173 additions and 125 deletions

View file

@ -9,7 +9,7 @@
namespace SafeExamBrowser.Browser.Contracts.Events namespace SafeExamBrowser.Browser.Contracts.Events
{ {
/// <summary> /// <summary>
/// Event handler used to indicate that the browser has detected a session identifier of a LMS. /// Event handler used to indicate that the browser has detected a user identifier of an LMS.
/// </summary> /// </summary>
public delegate void SessionIdentifierDetectedEventHandler(string identifier); public delegate void UserIdentifierDetectedEventHandler(string identifier);
} }

View file

@ -22,9 +22,9 @@ namespace SafeExamBrowser.Browser.Contracts
event DownloadRequestedEventHandler ConfigurationDownloadRequested; event DownloadRequestedEventHandler ConfigurationDownloadRequested;
/// <summary> /// <summary>
/// Event fired when the browser application detects a session identifier of an LMS. /// Event fired when the user tries to focus the taskbar.
/// </summary> /// </summary>
event SessionIdentifierDetectedEventHandler SessionIdentifierDetected; event LoseFocusRequestedEventHandler LoseFocusRequested;
/// <summary> /// <summary>
/// Event fired when the browser application detects a request to terminate SEB. /// Event fired when the browser application detects a request to terminate SEB.
@ -32,9 +32,9 @@ namespace SafeExamBrowser.Browser.Contracts
event TerminationRequestedEventHandler TerminationRequested; event TerminationRequestedEventHandler TerminationRequested;
/// <summary> /// <summary>
/// Event fired when the user tries to focus the taskbar. /// Event fired when the browser application detects a user identifier of an LMS.
/// </summary> /// </summary>
event LoseFocusRequestedEventHandler LoseFocusRequested; event UserIdentifierDetectedEventHandler UserIdentifierDetected;
/// <summary> /// <summary>
/// Transfers the focus to the browser application. If the parameter is <c>true</c>, the first focusable element in the browser window /// Transfers the focus to the browser application. If the parameter is <c>true</c>, the first focusable element in the browser window

View file

@ -60,7 +60,7 @@
<Compile Include="Events\DownloadRequestedEventHandler.cs" /> <Compile Include="Events\DownloadRequestedEventHandler.cs" />
<Compile Include="Events\TabPressedEventHandler.cs" /> <Compile Include="Events\TabPressedEventHandler.cs" />
<Compile Include="Events\LoseFocusRequestedEventHandler.cs" /> <Compile Include="Events\LoseFocusRequestedEventHandler.cs" />
<Compile Include="Events\SessionIdentifierDetectedEventHandler.cs" /> <Compile Include="Events\UserIdentifierDetectedEventHandler.cs" />
<Compile Include="Events\TerminationRequestedEventHandler.cs" /> <Compile Include="Events\TerminationRequestedEventHandler.cs" />
<Compile Include="Filters\IRequestFilter.cs" /> <Compile Include="Filters\IRequestFilter.cs" />
<Compile Include="Filters\IRule.cs" /> <Compile Include="Filters\IRule.cs" />

View file

@ -166,7 +166,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
[TestMethod] [TestMethod]
public void MustLetOperatingSystemHandleUnknownProtocols() public void MustLetOperatingSystemHandleUnknownProtocols()
{ {
Assert.IsTrue(sut.OnProtocolExecution(default(IWebBrowser), default(IBrowser), default(IFrame), default(IRequest))); Assert.IsTrue(sut.OnProtocolExecution(default, default, default, default));
} }
[TestMethod] [TestMethod]
@ -232,28 +232,28 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
var newUrl = default(string); var newUrl = default(string);
var request = new Mock<IRequest>(); var request = new Mock<IRequest>();
var response = new Mock<IResponse>(); var response = new Mock<IResponse>();
var sessionId = default(string); var userId = default(string);
headers.Add("X-LMS-USER-ID", "some-session-id-123"); headers.Add("X-LMS-USER-ID", "some-session-id-123");
request.SetupGet(r => r.Url).Returns("https://www.somelms.org"); request.SetupGet(r => r.Url).Returns("https://www.somelms.org");
response.SetupGet(r => r.Headers).Returns(headers); response.SetupGet(r => r.Headers).Returns(headers);
sut.SessionIdentifierDetected += (id) => sut.UserIdentifierDetected += (id) =>
{ {
sessionId = id; userId = id;
@event.Set(); @event.Set();
}; };
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl); sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("some-session-id-123", sessionId); Assert.AreEqual("some-session-id-123", userId);
headers.Clear(); headers.Clear();
headers.Add("X-LMS-USER-ID", "other-session-id-123"); headers.Add("X-LMS-USER-ID", "other-session-id-123");
sessionId = default(string); userId = default;
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object); sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("other-session-id-123", sessionId); Assert.AreEqual("other-session-id-123", userId);
} }
[TestMethod] [TestMethod]
@ -264,28 +264,28 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
var newUrl = default(string); var newUrl = default(string);
var request = new Mock<IRequest>(); var request = new Mock<IRequest>();
var response = new Mock<IResponse>(); var response = new Mock<IResponse>();
var sessionId = default(string); var userId = default(string);
headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-123\\\"}\"; expires"); headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-123\\\"}\"; expires");
request.SetupGet(r => r.Url).Returns("https://www.somelms.org"); request.SetupGet(r => r.Url).Returns("https://www.somelms.org");
response.SetupGet(r => r.Headers).Returns(headers); response.SetupGet(r => r.Headers).Returns(headers);
sut.SessionIdentifierDetected += (id) => sut.UserIdentifierDetected += (id) =>
{ {
sessionId = id; userId = id;
@event.Set(); @event.Set();
}; };
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl); sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("edx-123", sessionId); Assert.AreEqual("edx-123", userId);
headers.Clear(); headers.Clear();
headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-345\\\"}\"; expires"); headers.Add("Set-Cookie", "edx-user-info=\"{\\\"username\\\": \\\"edx-345\\\"}\"; expires");
sessionId = default(string); userId = default;
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object); sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("edx-345", sessionId); Assert.AreEqual("edx-345", userId);
} }
[TestMethod] [TestMethod]
@ -296,28 +296,28 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
var newUrl = default(string); var newUrl = default(string);
var request = new Mock<IRequest>(); var request = new Mock<IRequest>();
var response = new Mock<IResponse>(); var response = new Mock<IResponse>();
var sessionId = default(string); var userId = default(string);
headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=123"); headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=123");
request.SetupGet(r => r.Url).Returns("https://www.some-moodle-instance.org"); request.SetupGet(r => r.Url).Returns("https://www.some-moodle-instance.org");
response.SetupGet(r => r.Headers).Returns(headers); response.SetupGet(r => r.Headers).Returns(headers);
sut.SessionIdentifierDetected += (id) => sut.UserIdentifierDetected += (id) =>
{ {
sessionId = id; userId = id;
@event.Set(); @event.Set();
}; };
sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl); sut.OnResourceRedirect(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), Mock.Of<IRequest>(), response.Object, ref newUrl);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("123", sessionId); Assert.AreEqual("123", userId);
headers.Clear(); headers.Clear();
headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=456"); headers.Add("Location", "https://www.some-moodle-instance.org/moodle/login/index.php?testsession=456");
sessionId = default(string); userId = default;
sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object); sut.OnResourceResponse(Mock.Of<IWebBrowser>(), Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, response.Object);
@event.WaitOne(); @event.WaitOne();
Assert.AreEqual("456", sessionId); Assert.AreEqual("456", userId);
} }
private class TestableResourceHandler : ResourceHandler private class TestableResourceHandler : ResourceHandler

View file

@ -59,9 +59,9 @@ namespace SafeExamBrowser.Browser
public string Tooltip { get; private set; } public string Tooltip { get; private set; }
public event DownloadRequestedEventHandler ConfigurationDownloadRequested; public event DownloadRequestedEventHandler ConfigurationDownloadRequested;
public event SessionIdentifierDetectedEventHandler SessionIdentifierDetected;
public event LoseFocusRequestedEventHandler LoseFocusRequested; public event LoseFocusRequestedEventHandler LoseFocusRequested;
public event TerminationRequestedEventHandler TerminationRequested; public event TerminationRequestedEventHandler TerminationRequested;
public event UserIdentifierDetectedEventHandler UserIdentifierDetected;
public event WindowsChangedEventHandler WindowsChanged; public event WindowsChangedEventHandler WindowsChanged;
public BrowserApplication( public BrowserApplication(
@ -209,7 +209,7 @@ namespace SafeExamBrowser.Browser
window.ConfigurationDownloadRequested += (f, a) => ConfigurationDownloadRequested?.Invoke(f, a); window.ConfigurationDownloadRequested += (f, a) => ConfigurationDownloadRequested?.Invoke(f, a);
window.PopupRequested += Window_PopupRequested; window.PopupRequested += Window_PopupRequested;
window.ResetRequested += Window_ResetRequested; window.ResetRequested += Window_ResetRequested;
window.SessionIdentifierDetected += (i) => SessionIdentifierDetected?.Invoke(i); window.UserIdentifierDetected += (i) => UserIdentifierDetected?.Invoke(i);
window.TerminationRequested += () => TerminationRequested?.Invoke(); window.TerminationRequested += () => TerminationRequested?.Invoke();
window.LoseFocusRequested += (forward) => LoseFocusRequested?.Invoke(forward); window.LoseFocusRequested += (forward) => LoseFocusRequested?.Invoke(forward);

View file

@ -81,11 +81,11 @@ namespace SafeExamBrowser.Browser
internal event WindowClosedEventHandler Closed; internal event WindowClosedEventHandler Closed;
internal event DownloadRequestedEventHandler ConfigurationDownloadRequested; internal event DownloadRequestedEventHandler ConfigurationDownloadRequested;
internal event LoseFocusRequestedEventHandler LoseFocusRequested;
internal event PopupRequestedEventHandler PopupRequested; internal event PopupRequestedEventHandler PopupRequested;
internal event ResetRequestedEventHandler ResetRequested; internal event ResetRequestedEventHandler ResetRequested;
internal event SessionIdentifierDetectedEventHandler SessionIdentifierDetected;
internal event LoseFocusRequestedEventHandler LoseFocusRequested;
internal event TerminationRequestedEventHandler TerminationRequested; internal event TerminationRequestedEventHandler TerminationRequested;
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
public event IconChangedEventHandler IconChanged; public event IconChangedEventHandler IconChanged;
public event TitleChangedEventHandler TitleChanged; public event TitleChangedEventHandler TitleChanged;
@ -185,9 +185,9 @@ namespace SafeExamBrowser.Browser
keyboardHandler.ZoomInRequested += ZoomInRequested; keyboardHandler.ZoomInRequested += ZoomInRequested;
keyboardHandler.ZoomOutRequested += ZoomOutRequested; keyboardHandler.ZoomOutRequested += ZoomOutRequested;
keyboardHandler.ZoomResetRequested += ZoomResetRequested; keyboardHandler.ZoomResetRequested += ZoomResetRequested;
resourceHandler.SessionIdentifierDetected += (id) => SessionIdentifierDetected?.Invoke(id);
requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited; requestHandler.QuitUrlVisited += RequestHandler_QuitUrlVisited;
requestHandler.RequestBlocked += RequestHandler_RequestBlocked; requestHandler.RequestBlocked += RequestHandler_RequestBlocked;
resourceHandler.UserIdentifierDetected += (id) => UserIdentifierDetected?.Invoke(id);
InitializeRequestFilter(requestFilter); InitializeRequestFilter(requestFilter);

View file

@ -44,9 +44,9 @@ namespace SafeExamBrowser.Browser.Handlers
private IResourceHandler contentHandler; private IResourceHandler contentHandler;
private IResourceHandler pageHandler; private IResourceHandler pageHandler;
private string sessionIdentifier; private string userIdentifier;
internal event SessionIdentifierDetectedEventHandler SessionIdentifierDetected; internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
internal ResourceHandler( internal ResourceHandler(
AppConfig appConfig, AppConfig appConfig,
@ -100,7 +100,7 @@ namespace SafeExamBrowser.Browser.Handlers
{ {
if (sessionMode == SessionMode.Server) if (sessionMode == SessionMode.Server)
{ {
SearchSessionIdentifiers(request, response); SearchUserIdentifier(request, response);
} }
base.OnResourceRedirect(chromiumWebBrowser, browser, frame, request, response, ref newUrl); base.OnResourceRedirect(chromiumWebBrowser, browser, frame, request, response, ref newUrl);
@ -117,7 +117,7 @@ namespace SafeExamBrowser.Browser.Handlers
if (sessionMode == SessionMode.Server) if (sessionMode == SessionMode.Server)
{ {
SearchSessionIdentifiers(request, response); SearchUserIdentifier(request, response);
} }
return base.OnResourceResponse(webBrowser, browser, frame, request, response); return base.OnResourceResponse(webBrowser, browser, frame, request, response);
@ -233,41 +233,46 @@ namespace SafeExamBrowser.Browser.Handlers
} }
} }
private void SearchSessionIdentifiers(IRequest request, IResponse response) private void SearchUserIdentifier(IRequest request, IResponse response)
{ {
var success = TrySearchGenericSessionIdentifier(response); var success = TrySearchGenericUserIdentifier(response);
if (!success) if (!success)
{ {
SearchEdxIdentifier(response); success = TrySearchEdxUserIdentifier(response);
SearchMoodleIdentifier(request, response); }
if (!success)
{
TrySearchMoodleUserIdentifier(request, response);
} }
} }
private bool TrySearchGenericSessionIdentifier(IResponse response) private bool TrySearchGenericUserIdentifier(IResponse response)
{ {
var ids = response.Headers.GetValues("X-LMS-USER-ID"); var ids = response.Headers.GetValues("X-LMS-USER-ID");
var success = false;
if (ids != default(string[])) if (ids != default(string[]))
{ {
var userId = ids.FirstOrDefault(); var userId = ids.FirstOrDefault();
if (userId != default && sessionIdentifier != userId) if (userId != default && userIdentifier != userId)
{ {
sessionIdentifier = userId; userIdentifier = userId;
Task.Run(() => SessionIdentifierDetected?.Invoke(sessionIdentifier)); Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Generic LMS session detected."); logger.Info("Generic LMS user identifier detected.");
success = true;
return true;
} }
} }
return false; return success;
} }
private void SearchEdxIdentifier(IResponse response) private bool TrySearchEdxUserIdentifier(IResponse response)
{ {
var cookies = response.Headers.GetValues("Set-Cookie"); var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[])) if (cookies != default(string[]))
{ {
@ -284,32 +289,42 @@ namespace SafeExamBrowser.Browser.Handlers
var json = JsonConvert.DeserializeObject(sanitized) as JObject; var json = JsonConvert.DeserializeObject(sanitized) as JObject;
var userName = json["username"].Value<string>(); var userName = json["username"].Value<string>();
if (sessionIdentifier != userName) if (userIdentifier != userName)
{ {
sessionIdentifier = userName; userIdentifier = userName;
Task.Run(() => SessionIdentifierDetected?.Invoke(sessionIdentifier)); Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("EdX session detected."); logger.Info("EdX user identifier detected.");
success = true;
} }
} }
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error("Failed to parse edX session identifier!", e); logger.Error("Failed to parse edX user identifier!", e);
} }
} }
return success;
} }
private void SearchMoodleIdentifier(IRequest request, IResponse response) private bool TrySearchMoodleUserIdentifier(IRequest request, IResponse response)
{ {
var success = TrySearchByLocation(response); var success = TrySearchMoodleUserIdentifierByLocation(response);
if (!success) if (!success)
{ {
TrySearchBySession(request, response); success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Plugin, request, response);
} }
if (!success)
{
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Theme, request, response);
}
return success;
} }
private bool TrySearchByLocation(IResponse response) private bool TrySearchMoodleUserIdentifierByLocation(IResponse response)
{ {
var locations = response.Headers.GetValues("Location"); var locations = response.Headers.GetValues("Location");
@ -323,11 +338,11 @@ namespace SafeExamBrowser.Browser.Handlers
{ {
var userId = location.Substring(location.IndexOf("=") + 1); var userId = location.Substring(location.IndexOf("=") + 1);
if (sessionIdentifier != userId) if (userIdentifier != userId)
{ {
sessionIdentifier = userId; userIdentifier = userId;
Task.Run(() => SessionIdentifierDetected?.Invoke(sessionIdentifier)); Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
logger.Info("Moodle session detected."); logger.Info("Moodle user identifier detected by location.");
} }
return true; return true;
@ -335,16 +350,17 @@ namespace SafeExamBrowser.Browser.Handlers
} }
catch (Exception e) catch (Exception e)
{ {
logger.Error("Failed to parse Moodle session identifier!", e); logger.Error("Failed to parse Moodle user identifier by location!", e);
} }
} }
return false; return false;
} }
private void TrySearchBySession(IRequest request, IResponse response) private bool TrySearchMoodleUserIdentifierByRequest(MoodleRequestType type, IRequest request, IResponse response)
{ {
var cookies = response.Headers.GetValues("Set-Cookie"); var cookies = response.Headers.GetValues("Set-Cookie");
var success = false;
if (cookies != default(string[])) if (cookies != default(string[]))
{ {
@ -352,51 +368,83 @@ namespace SafeExamBrowser.Browser.Handlers
if (session != default) if (session != default)
{ {
var requestUrl = request.Url; var userId = ExecuteMoodleUserIdentifierRequest(request.Url, session, type);
Task.Run(async () => if (int.TryParse(userId, out var id) && id > 0 && userIdentifier != userId)
{ {
try userIdentifier = userId;
{ Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
var start = session.IndexOf("=") + 1; logger.Info($"Moodle user identifier detected by request ({type}).");
var end = session.IndexOf(";"); success = true;
var value = session.Substring(start, end - start); }
var uri = new Uri(requestUrl);
var message = new HttpRequestMessage(HttpMethod.Get, $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/theme/boost_ethz/sebuser.php");
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"MoodleSession={value}");
var result = await client.SendAsync(message);
if (result.IsSuccessStatusCode)
{
var userId = await result.Content.ReadAsStringAsync();
if (int.TryParse(userId, out var id) && id > 0 && sessionIdentifier != userId)
{
#pragma warning disable CS4014
sessionIdentifier = userId;
Task.Run(() => SessionIdentifierDetected?.Invoke(sessionIdentifier));
logger.Info("Moodle session detected.");
#pragma warning restore CS4014
}
}
else
{
logger.Error($"Failed to retrieve Moodle session identifier! Response: {result.StatusCode} {result.ReasonPhrase}");
}
}
}
catch (Exception e)
{
logger.Error("Failed to parse Moodle session identifier!", e);
}
});
} }
} }
return success;
}
private string ExecuteMoodleUserIdentifierRequest(string requestUrl, string session, MoodleRequestType type)
{
var userId = default(string);
try
{
Task.Run(async () =>
{
try
{
var endpointUrl = default(string);
var start = session.IndexOf("=") + 1;
var end = session.IndexOf(";");
var value = session.Substring(start, end - start);
var uri = new Uri(requestUrl);
if (type == MoodleRequestType.Plugin)
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/mod/quiz/accessrule/sebserver/classes/external/user.php";
}
else
{
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/theme/boost_ethz/sebuser.php";
}
var message = new HttpRequestMessage(HttpMethod.Get, endpointUrl);
using (var handler = new HttpClientHandler { UseCookies = false })
using (var client = new HttpClient(handler))
{
message.Headers.Add("Cookie", $"MoodleSession={value}");
var result = await client.SendAsync(message);
if (result.IsSuccessStatusCode)
{
userId = await result.Content.ReadAsStringAsync();
}
else if (result.StatusCode != HttpStatusCode.NotFound)
{
logger.Error($"Failed to retrieve Moodle user identifier by request ({type})! Response: {(int) result.StatusCode} {result.ReasonPhrase}");
}
}
}
catch (Exception e)
{
logger.Error($"Failed to parse Moodle user identifier by request ({type})!", e);
}
}).GetAwaiter().GetResult();
}
catch (Exception e)
{
logger.Error($"Failed to execute Moodle user identifier request ({type})!", e);
}
return userId;
}
private enum MoodleRequestType
{
Plugin,
Theme
} }
} }
} }

View file

@ -324,18 +324,18 @@ namespace SafeExamBrowser.Client.UnitTests
} }
[TestMethod] [TestMethod]
public void Browser_MustHandleSessionIdentifierDetection() public void Browser_MustHandleUserIdentifierDetection()
{ {
var counter = 0; var counter = 0;
var identifier = "abc123"; var identifier = "abc123";
settings.SessionMode = SessionMode.Server; settings.SessionMode = SessionMode.Server;
server.Setup(s => s.SendSessionIdentifier(It.IsAny<string>())).Returns(() => new ServerResponse(++counter == 3)); server.Setup(s => s.SendUserIdentifier(It.IsAny<string>())).Returns(() => new ServerResponse(++counter == 3));
sut.TryStart(); sut.TryStart();
browser.Raise(b => b.SessionIdentifierDetected += null, identifier); browser.Raise(b => b.UserIdentifierDetected += null, identifier);
server.Verify(s => s.SendSessionIdentifier(It.Is<string>(id => id == identifier)), Times.Exactly(3)); server.Verify(s => s.SendUserIdentifier(It.Is<string>(id => id == identifier)), Times.Exactly(3));
} }
[TestMethod] [TestMethod]

View file

@ -200,9 +200,9 @@ namespace SafeExamBrowser.Client
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted; applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
applicationMonitor.TerminationFailed += ApplicationMonitor_TerminationFailed; applicationMonitor.TerminationFailed += ApplicationMonitor_TerminationFailed;
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested; Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
Browser.SessionIdentifierDetected += Browser_SessionIdentifierDetected;
Browser.TerminationRequested += Browser_TerminationRequested;
Browser.LoseFocusRequested += Browser_LoseFocusRequested; Browser.LoseFocusRequested += Browser_LoseFocusRequested;
Browser.TerminationRequested += Browser_TerminationRequested;
Browser.UserIdentifierDetected += Browser_UserIdentifierDetected;
ClientHost.ExamSelectionRequested += ClientHost_ExamSelectionRequested; ClientHost.ExamSelectionRequested += ClientHost_ExamSelectionRequested;
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested; ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
ClientHost.PasswordRequested += ClientHost_PasswordRequested; ClientHost.PasswordRequested += ClientHost_PasswordRequested;
@ -254,9 +254,9 @@ namespace SafeExamBrowser.Client
if (Browser != null) if (Browser != null)
{ {
Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested; Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested;
Browser.SessionIdentifierDetected -= Browser_SessionIdentifierDetected;
Browser.TerminationRequested -= Browser_TerminationRequested;
Browser.LoseFocusRequested -= Browser_LoseFocusRequested; Browser.LoseFocusRequested -= Browser_LoseFocusRequested;
Browser.TerminationRequested -= Browser_TerminationRequested;
Browser.UserIdentifierDetected -= Browser_UserIdentifierDetected;
} }
if (ClientHost != null) if (ClientHost != null)
@ -531,17 +531,17 @@ namespace SafeExamBrowser.Client
} }
} }
private void Browser_SessionIdentifierDetected(string identifier) private void Browser_UserIdentifierDetected(string identifier)
{ {
if (Settings.SessionMode == SessionMode.Server) if (Settings.SessionMode == SessionMode.Server)
{ {
var response = Server.SendSessionIdentifier(identifier); var response = Server.SendUserIdentifier(identifier);
while (!response.Success) while (!response.Success)
{ {
logger.Error($"Failed to communicate session identifier with server! {response.Message}"); logger.Error($"Failed to communicate user identifier with server! {response.Message}");
Thread.Sleep(Settings.Server.RequestAttemptInterval); Thread.Sleep(Settings.Server.RequestAttemptInterval);
response = Server.SendSessionIdentifier(identifier); response = Server.SendUserIdentifier(identifier);
} }
} }
} }

View file

@ -110,9 +110,9 @@ namespace SafeExamBrowser.Server.Contracts
ServerResponse<string> SendSelectedExam(Exam exam); ServerResponse<string> SendSelectedExam(Exam exam);
/// <summary> /// <summary>
/// Sends the given user session identifier of a LMS and thus establishes a connection with the server. /// Sends the given user identifier of an LMS and thus establishes a connection with the server.
/// </summary> /// </summary>
ServerResponse SendSessionIdentifier(string identifier); ServerResponse SendUserIdentifier(string identifier);
/// <summary> /// <summary>
/// Starts sending ping and log data to the server. /// Starts sending ping and log data to the server.

View file

@ -13,9 +13,9 @@ using SafeExamBrowser.Settings.Server;
namespace SafeExamBrowser.Server.Requests namespace SafeExamBrowser.Server.Requests
{ {
internal class SessionIdentifierRequest : BaseRequest internal class UserIdentifierRequest : BaseRequest
{ {
internal SessionIdentifierRequest( internal UserIdentifierRequest(
ApiVersion1 api, ApiVersion1 api,
HttpClient httpClient, HttpClient httpClient,
ILogger logger, ILogger logger,

View file

@ -85,7 +85,7 @@
<Compile Include="Requests\PowerSupplyRequest.cs" /> <Compile Include="Requests\PowerSupplyRequest.cs" />
<Compile Include="Requests\RaiseHandRequest.cs" /> <Compile Include="Requests\RaiseHandRequest.cs" />
<Compile Include="Requests\SelectExamRequest.cs" /> <Compile Include="Requests\SelectExamRequest.cs" />
<Compile Include="Requests\SessionIdentifierRequest.cs" /> <Compile Include="Requests\UserIdentifierRequest.cs" />
<Compile Include="ServerProxy.cs" /> <Compile Include="ServerProxy.cs" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View file

@ -310,18 +310,18 @@ namespace SafeExamBrowser.Server
return new ServerResponse<string>(success, browserExamKey, message); return new ServerResponse<string>(success, browserExamKey, message);
} }
public ServerResponse SendSessionIdentifier(string identifier) public ServerResponse SendUserIdentifier(string identifier)
{ {
var request = new SessionIdentifierRequest(api, httpClient, logger, parser, settings); var request = new UserIdentifierRequest(api, httpClient, logger, parser, settings);
var success = request.TryExecute(examId, identifier, out var message); var success = request.TryExecute(examId, identifier, out var message);
if (success) if (success)
{ {
logger.Info("Successfully sent session identifier."); logger.Info("Successfully sent user identifier.");
} }
else else
{ {
logger.Error("Failed to send session identifier!"); logger.Error("Failed to send user identifier!");
} }
return new ServerResponse(success, message); return new ServerResponse(success, message);