Compare commits
53 commits
Author | SHA1 | Date | |
---|---|---|---|
|
7029b12d81 | ||
|
9762019499 | ||
|
d5b182ae2f | ||
|
4c0f3cfa6c | ||
|
f096b96741 | ||
|
50ac28f9ea | ||
|
144c3ba752 | ||
|
21353e6d6d | ||
|
26f14f235d | ||
|
1ff7d84375 | ||
|
febfd944e0 | ||
|
a1bfaadcd9 | ||
|
0b1746a82e | ||
|
ede6a926cc | ||
|
d4f5f203db | ||
|
a350949b1b | ||
|
6a77a41564 | ||
|
1f50ab74c9 | ||
|
b48ef21708 | ||
|
f3a9030505 | ||
|
89091acaac | ||
|
04843d3fa8 | ||
|
a3c9271faf | ||
|
68d6d47fe6 | ||
|
b4366adab1 | ||
|
c23b78488c | ||
|
58c8e69716 | ||
|
0fb7f23bcb | ||
|
05f46cd6b4 | ||
|
f2798581a4 | ||
|
471e69d460 | ||
|
62dc690a52 | ||
|
a41b40d428 | ||
|
8cf214b39c | ||
|
04dce13d86 | ||
|
767ac84391 | ||
|
84bbcb82ef | ||
|
b3228aedef | ||
|
3b099688f7 | ||
|
639700abd8 | ||
|
473edc7a2e | ||
|
60ee95a9ee | ||
|
1edde7b6f5 | ||
|
4015e9a574 | ||
|
bbb5ec2571 | ||
|
8b3f9b0838 | ||
|
e4a82e2f63 | ||
|
01db8fd84e | ||
|
a397446252 | ||
|
e8ebd2840e | ||
|
d9662ec31e | ||
|
c2f61ea6ab | ||
|
7801d68b97 |
183 changed files with 4237 additions and 2992 deletions
2
.github/workflows/issues.yml
vendored
2
.github/workflows/issues.yml
vendored
|
@ -21,5 +21,5 @@ jobs:
|
|||
close-issue-message: "This issue was closed because it has been inactive for 14 days since being marked as stale."
|
||||
days-before-pr-stale: -1
|
||||
days-before-pr-close: -1
|
||||
exempt-issue-labels: "bug,enhancement,feature request"
|
||||
exempt-issue-labels: "bug,enhancement,feature request,known issue"
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
|
Binary file not shown.
|
@ -7,7 +7,6 @@ Refactored version of Safe Exam Browser for Windows with Chromium as integrated
|
|||
SEB 3.x requires the prerequisites listed below in order to work correctly. These are automatically installed with the setup bundle and need only be manually installed when using the MSI packages.
|
||||
|
||||
* .NET Framework 4.8 Runtime: https://dotnet.microsoft.com/download/dotnet-framework/net48
|
||||
* Microsoft Edge WebView2 Runtime: https://go.microsoft.com/fwlink/p/?LinkId=2124703
|
||||
* Visual C++ 2015-2019 Redistributable: https://support.microsoft.com/en-us/help/2977003/the-latest-supported-visual-c-downloads
|
||||
|
||||
## Project Status
|
||||
|
@ -17,7 +16,7 @@ SEB 3.x requires the prerequisites listed below in order to work correctly. Thes
|
|||
|
||||
| Aspect | Status | Details |
|
||||
| ----------------- | --------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------- |
|
||||
| Development Build | ![Development Build Status](https://sebdev-let.ethz.ch/api/projects/status/kq78qrjtnpk82ti0?svg=true) | https://sebdev-let.ethz.ch/project/appveyor/seb-win-refactoring |
|
||||
| Development Build | ![Development Build Status](https://sebdev.ethz.ch/api/projects/status/kq78qrjtnpk82ti0?svg=true) | https://sebdev.ethz.ch/project/appveyor/seb-win-refactoring |
|
||||
| Test Build | ![Test Build Status](https://ci.appveyor.com/api/projects/status/a56akt9r174570m7?svg=true) | https://ci.appveyor.com/project/dbuechel/seb-win-refactoring |
|
||||
| Test Run | ![AppVeyor Tests](https://img.shields.io/appveyor/tests/dbuechel/seb-win-refactoring?logo=appveyor&logoColor=%23ccc) | https://ci.appveyor.com/project/dbuechel/seb-win-refactoring |
|
||||
| Code Coverage | ![Code Coverage](https://codecov.io/gh/SafeExamBrowser/seb-win-refactoring/branch/master/graph/badge.svg) | https://codecov.io/gh/SafeExamBrowser/seb-win-refactoring |
|
||||
|
|
35
SECURITY.md
Normal file
35
SECURITY.md
Normal file
|
@ -0,0 +1,35 @@
|
|||
# Security Policy
|
||||
|
||||
We only support the latest official relese version with respect to security vulnerabilities. Thus, only the latest or then the upcoming next release version
|
||||
will receive vulnerability fixes and security updates. A vulnerability may however be reported for any version, unless it already has been fixed with a later
|
||||
release version.
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
> [!IMPORTANT]
|
||||
> - Please _always_ verify that no later release version exists which fixes the vulnerability.
|
||||
> - Please _always_ consult the documentation first before creating a vulnerability report: https://safeexambrowser.org/windows/win_usermanual_en.html.
|
||||
> - Please _always_ attach the log file(s) of the affected session(s)! They can be found under `%LocalAppData%\SafeExamBrowser\Logs`.
|
||||
|
||||
**Describe the Vulnerability**
|
||||
A clear and concise description of what the vulnerability is.
|
||||
|
||||
**Steps to Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See ...
|
||||
|
||||
**Expected Behavior**
|
||||
A clear and concise description of what you expected to happen.
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
|
||||
**Version Information**
|
||||
- OS: [e.g. Windows 10 Professional, Version 1803]
|
||||
- SEB-Version [e.g. SEB 3.0.1]
|
||||
|
||||
**Additional Context**
|
||||
Add any other context about the vulnerability here.
|
|
@ -6,8 +6,6 @@
|
|||
<Import Project="..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="..\packages\cef.redist.x86.120.2.7\build\cef.redist.x86.props" Condition="Exists('..\packages\cef.redist.x86.120.2.7\build\cef.redist.x86.props')" />
|
||||
<Import Project="..\packages\cef.redist.x64.120.2.7\build\cef.redist.x64.props" Condition="Exists('..\packages\cef.redist.x64.120.2.7\build\cef.redist.x64.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
|
@ -206,8 +204,6 @@
|
|||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\cef.redist.x64.120.2.7\build\cef.redist.x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\cef.redist.x64.120.2.7\build\cef.redist.x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\cef.redist.x86.120.2.7\build\cef.redist.x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\cef.redist.x86.120.2.7\build\cef.redist.x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.121.3.13\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.121.3.13\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.121.3.130\build\CefSharp.Common.props'))" />
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
|
||||
<package id="cef.redist.x64" version="120.2.7" targetFramework="net48" />
|
||||
<package id="cef.redist.x86" version="120.2.7" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="121.3.130" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="121.3.13" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="121.3.13" targetFramework="net48" />
|
||||
|
|
|
@ -18,6 +18,7 @@ namespace SafeExamBrowser.Browser.Content
|
|||
|
||||
private string api;
|
||||
private string clipboard;
|
||||
private string pageZoom;
|
||||
|
||||
internal ContentLoader(IText text)
|
||||
{
|
||||
|
@ -97,5 +98,22 @@ namespace SafeExamBrowser.Browser.Content
|
|||
|
||||
return clipboard;
|
||||
}
|
||||
|
||||
internal string LoadPageZoom()
|
||||
{
|
||||
if (pageZoom == default)
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(ContentLoader));
|
||||
var path = $"{typeof(ContentLoader).Namespace}.PageZoom.js";
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(path))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
pageZoom = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
return pageZoom;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
16
SafeExamBrowser.Browser/Content/PageZoom.js
Normal file
16
SafeExamBrowser.Browser/Content/PageZoom.js
Normal file
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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/.
|
||||
*/
|
||||
|
||||
function disableMouseWheelZoom(e) {
|
||||
if (e.ctrlKey) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener('wheel', disableMouseWheelZoom, { passive: false });
|
|
@ -138,6 +138,11 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
filePath = Path.Combine(KnownFolders.Downloads.ExpandedPath, downloadItem.SuggestedFileName);
|
||||
}
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
filePath = AppendIndexSuffixTo(filePath);
|
||||
}
|
||||
|
||||
if (showDialog)
|
||||
{
|
||||
logger.Debug($"Allowing user to select custom download location, with '{filePath}' as suggestion.");
|
||||
|
@ -155,6 +160,26 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
}
|
||||
}
|
||||
|
||||
private string AppendIndexSuffixTo(string filePath)
|
||||
{
|
||||
var directory = Path.GetDirectoryName(filePath);
|
||||
var extension = Path.GetExtension(filePath);
|
||||
var name = Path.GetFileNameWithoutExtension(filePath);
|
||||
var path = default(string);
|
||||
|
||||
for (var suffix = 1; suffix < int.MaxValue; suffix++)
|
||||
{
|
||||
path = Path.Combine(directory, $"{name}({suffix}){extension}");
|
||||
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
private void RequestConfigurationFileDownload(DownloadItem downloadItem, IBeforeDownloadCallback callback)
|
||||
{
|
||||
var args = new DownloadEventArgs { Url = downloadItem.Url };
|
||||
|
|
|
@ -40,9 +40,15 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
var configurationKey = keyGenerator.CalculateConfigurationKeyHash(settings.ConfigurationKey, frame.Url);
|
||||
var api = contentLoader.LoadApi(browserExamKey, configurationKey, appConfig.ProgramBuildVersion);
|
||||
var clipboardScript = contentLoader.LoadClipboard();
|
||||
var pageZoomScript = contentLoader.LoadPageZoom();
|
||||
|
||||
frame.ExecuteJavaScriptAsync(api);
|
||||
|
||||
if (!settings.AllowPageZoom)
|
||||
{
|
||||
frame.ExecuteJavaScriptAsync(pageZoomScript);
|
||||
}
|
||||
|
||||
if (!settings.AllowPrint)
|
||||
{
|
||||
frame.ExecuteJavaScriptAsync($"window.print = function() {{ alert('{text.Get(TextKey.Browser_PrintNotAllowed)}') }}");
|
||||
|
|
|
@ -396,6 +396,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
var endpointUrl = default(string);
|
||||
var start = session.IndexOf("=") + 1;
|
||||
var end = session.IndexOf(";");
|
||||
var name = session.Substring(0, start - 1);
|
||||
var value = session.Substring(start, end - start);
|
||||
var uri = new Uri(requestUrl);
|
||||
|
||||
|
@ -413,7 +414,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
using (var handler = new HttpClientHandler { UseCookies = false })
|
||||
using (var client = new HttpClient(handler))
|
||||
{
|
||||
message.Headers.Add("Cookie", $"MoodleSession={value}");
|
||||
message.Headers.Add("Cookie", $"{name}={value}");
|
||||
|
||||
var result = await client.SendAsync(message);
|
||||
|
||||
|
|
|
@ -194,7 +194,9 @@
|
|||
<ItemGroup>
|
||||
<EmbeddedResource Include="Content\Clipboard.js" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Content\PageZoom.js" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
|
|
@ -14,6 +14,7 @@ using Moq;
|
|||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Operations.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
|
@ -33,7 +34,8 @@ using SafeExamBrowser.Server.Contracts;
|
|||
using SafeExamBrowser.Server.Contracts.Data;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Monitoring;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
using SafeExamBrowser.Settings.UserInterface;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
@ -54,6 +56,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
private Mock<IBrowserApplication> browser;
|
||||
private Mock<IClientHost> clientHost;
|
||||
private ClientContext context;
|
||||
private Mock<ICoordinator> coordinator;
|
||||
private Mock<IDisplayMonitor> displayMonitor;
|
||||
private Mock<IExplorerShell> explorerShell;
|
||||
private Mock<IFileSystemDialog> fileSystemDialog;
|
||||
|
@ -61,15 +64,15 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
private Mock<IIntegrityModule> integrityModule;
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<INetworkAdapter> networkAdapter;
|
||||
private Mock<IOperationSequence> operationSequence;
|
||||
private Mock<IRegistry> registry;
|
||||
private Mock<IRuntimeProxy> runtimeProxy;
|
||||
private Mock<ISystemSentinel> sentinel;
|
||||
private Mock<IServerProxy> server;
|
||||
private Guid sessionId;
|
||||
private AppSettings settings;
|
||||
private Mock<Action> shutdown;
|
||||
private Mock<ISplashScreen> splashScreen;
|
||||
private Mock<ISystemMonitor> systemMonitor;
|
||||
private Mock<ITaskbar> taskbar;
|
||||
private Mock<IText> text;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
@ -87,6 +90,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
browser = new Mock<IBrowserApplication>();
|
||||
clientHost = new Mock<IClientHost>();
|
||||
context = new ClientContext();
|
||||
coordinator = new Mock<ICoordinator>();
|
||||
displayMonitor = new Mock<IDisplayMonitor>();
|
||||
explorerShell = new Mock<IExplorerShell>();
|
||||
fileSystemDialog = new Mock<IFileSystemDialog>();
|
||||
|
@ -94,15 +98,15 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
integrityModule = new Mock<IIntegrityModule>();
|
||||
logger = new Mock<ILogger>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
networkAdapter = new Mock<INetworkAdapter>();
|
||||
operationSequence = new Mock<IOperationSequence>();
|
||||
registry = new Mock<IRegistry>();
|
||||
runtimeProxy = new Mock<IRuntimeProxy>();
|
||||
sentinel = new Mock<ISystemSentinel>();
|
||||
server = new Mock<IServerProxy>();
|
||||
sessionId = Guid.NewGuid();
|
||||
settings = new AppSettings();
|
||||
shutdown = new Mock<Action>();
|
||||
splashScreen = new Mock<ISplashScreen>();
|
||||
systemMonitor = new Mock<ISystemMonitor>();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
@ -116,18 +120,19 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
actionCenter.Object,
|
||||
applicationMonitor.Object,
|
||||
context,
|
||||
coordinator.Object,
|
||||
displayMonitor.Object,
|
||||
explorerShell.Object,
|
||||
fileSystemDialog.Object,
|
||||
hashAlgorithm.Object,
|
||||
logger.Object,
|
||||
messageBox.Object,
|
||||
networkAdapter.Object,
|
||||
operationSequence.Object,
|
||||
registry.Object,
|
||||
runtimeProxy.Object,
|
||||
shutdown.Object,
|
||||
splashScreen.Object,
|
||||
systemMonitor.Object,
|
||||
sentinel.Object,
|
||||
taskbar.Object,
|
||||
text.Object,
|
||||
uiFactory.Object);
|
||||
|
@ -151,7 +156,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var shell = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
|
||||
|
@ -185,7 +190,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var shell = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.Taskbar.EnableTaskbar = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
|
||||
|
@ -218,9 +223,9 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtimeProxy.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>()))
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object)
|
||||
.Callback<string, string, IEnumerable<LockScreenOption>>((m, t, o) => result.OptionId = o.First().Id);
|
||||
.Callback<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.First().Id);
|
||||
|
||||
sut.TryStart();
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
@ -237,9 +242,9 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtimeProxy.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>()))
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object)
|
||||
.Callback<string, string, IEnumerable<LockScreenOption>>((m, t, o) => result.OptionId = o.Last().Id);
|
||||
.Callback<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.Last().Id);
|
||||
|
||||
sut.TryStart();
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
@ -271,7 +276,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
lockScreen.Setup(l => l.WaitForResult()).Callback(() => wait = ++order).Returns(result);
|
||||
lockScreen.Setup(l => l.Close()).Callback(() => close = ++order);
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>()))
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
|
@ -308,7 +313,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
hashAlgorithm.Setup(a => a.GenerateHashFor(It.Is<string>(p => p == result.Password))).Returns(hash);
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(lockScreenResult);
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>()))
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
|
@ -497,7 +502,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var order = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
displayMonitor.Setup(m => m.InitializePrimaryDisplay(It.Is<int>(h => h == height))).Callback(() => workingArea = ++order);
|
||||
|
@ -528,7 +533,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var order = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.Taskbar.EnableTaskbar = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
actionCenter.Setup(t => t.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == 0))).Callback(() => workingArea = ++order);
|
||||
|
@ -557,7 +562,9 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
|
||||
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = false });
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
|
||||
uiFactory.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>())).Returns(lockScreen.Object);
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
displayMonitor.Raise(d => d.DisplayChanged += null);
|
||||
|
@ -722,13 +729,17 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
runtimeProxy.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
args.Callback(true, string.Empty);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
}
|
||||
|
||||
|
@ -745,7 +756,30 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustNotAllowConcurrentExecution()
|
||||
{
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(false);
|
||||
runtimeProxy.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
args.Callback?.Invoke(true, string.Empty);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
|
@ -755,6 +789,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var args = new DownloadEventArgs { Url = "sebs://www.somehost.org/some/path/some_configuration.seb?query=123" };
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
settings.Security.QuitPasswordHash = "abc123";
|
||||
settings.Security.ReconfigurationUrl = "sebs://www.somehost.org/some/path/*.seb?query=123";
|
||||
|
@ -764,7 +799,10 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
args.Callback(true, string.Empty);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
}
|
||||
|
||||
|
@ -779,7 +817,10 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
|
@ -795,7 +836,10 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
|
@ -808,7 +852,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
|
@ -818,11 +862,14 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
runtimeProxy.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
|
||||
args.Callback(true, downloadUrl, downloadPath);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.Is<string>(p => p == downloadPath), It.Is<string>(u => u == downloadUrl)), Times.Once);
|
||||
|
||||
Assert.AreEqual(downloadPath, args.DownloadPath);
|
||||
|
@ -838,7 +885,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
|
@ -848,11 +895,14 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
runtimeProxy.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
|
||||
args.Callback(false, downloadPath);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Once);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
|
@ -865,7 +915,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
|
@ -875,18 +925,21 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
runtimeProxy.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(false));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
sut.TryStart();
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, filename, args);
|
||||
args.Callback(true, downloadUrl, downloadPath);
|
||||
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Once);
|
||||
messageBox.Verify(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Error),
|
||||
It.IsAny<IWindow>()), Times.Once);
|
||||
runtimeProxy.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -942,8 +995,8 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
[TestMethod]
|
||||
public void Shutdown_MustCloseActionCenterAndTaskbarIfEnabled()
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = true;
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Terminate();
|
||||
|
||||
|
@ -954,8 +1007,8 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
[TestMethod]
|
||||
public void Shutdown_MustNotCloseActionCenterAndTaskbarIfNotEnabled()
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = false;
|
||||
settings.Taskbar.EnableTaskbar = false;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
sut.Terminate();
|
||||
|
||||
|
@ -1126,7 +1179,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
[TestMethod]
|
||||
public void Startup_MustCorrectlyShowTaskbar()
|
||||
{
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
sut.TryStart();
|
||||
|
||||
taskbar.Verify(t => t.Show(), Times.Once);
|
||||
|
@ -1138,7 +1191,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
taskbar.Verify(t => t.Show(), Times.Never);
|
||||
|
||||
taskbar.Reset();
|
||||
settings.Taskbar.EnableTaskbar = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
|
||||
sut.TryStart();
|
||||
|
||||
|
@ -1148,7 +1201,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
[TestMethod]
|
||||
public void Startup_MustCorrectlyShowActionCenter()
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
sut.TryStart();
|
||||
|
||||
actionCenter.Verify(t => t.Promote(), Times.Once);
|
||||
|
@ -1162,7 +1215,7 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
actionCenter.Verify(t => t.Show(), Times.Never);
|
||||
|
||||
actionCenter.Reset();
|
||||
settings.ActionCenter.EnableActionCenter = false;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
operationSequence.Setup(o => o.TryPerform()).Returns(OperationResult.Success);
|
||||
sut.TryStart();
|
||||
|
||||
|
@ -1224,13 +1277,18 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
|
||||
settings.Service.IgnoreService = true;
|
||||
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
|
||||
uiFactory.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>())).Returns(lockScreen.Object);
|
||||
settings.Service.IgnoreService = true;
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
systemMonitor.Raise(m => m.SessionChanged += null);
|
||||
sentinel.Raise(s => s.SessionChanged += null);
|
||||
|
||||
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
}
|
||||
|
||||
|
@ -1240,17 +1298,20 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult();
|
||||
|
||||
settings.Service.IgnoreService = true;
|
||||
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtimeProxy.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
settings.Service.IgnoreService = true;
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>()))
|
||||
.Callback(new Action<string, string, IEnumerable<LockScreenOption>>((message, title, options) => result.OptionId = options.Last().Id))
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Callback(new Action<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((message, title, options, settings) => result.OptionId = options.Last().Id))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
systemMonitor.Raise(m => m.SessionChanged += null);
|
||||
sentinel.Raise(s => s.SessionChanged += null);
|
||||
|
||||
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
runtimeProxy.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
@ -1264,10 +1325,12 @@ namespace SafeExamBrowser.Client.UnitTests
|
|||
settings.Service.DisableUserLock = false;
|
||||
settings.Service.DisableUserSwitch = false;
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
|
||||
uiFactory.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>())).Returns(lockScreen.Object);
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sut.TryStart();
|
||||
systemMonitor.Raise(m => m.SessionChanged += null);
|
||||
sentinel.Raise(s => s.SessionChanged += null);
|
||||
|
||||
lockScreen.Verify(l => l.Show(), Times.Never);
|
||||
}
|
||||
|
|
118
SafeExamBrowser.Client.UnitTests/CoordinatorTests.cs
Normal file
118
SafeExamBrowser.Client.UnitTests/CoordinatorTests.cs
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests
|
||||
{
|
||||
[TestClass]
|
||||
public class CoordinatorTests
|
||||
{
|
||||
private Coordinator sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
sut = new Coordinator();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ReconfigurationLock_MustWorkCorrectly()
|
||||
{
|
||||
Assert.IsFalse(sut.IsReconfigurationLocked());
|
||||
|
||||
sut.RequestReconfigurationLock();
|
||||
|
||||
var result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
Assert.IsTrue(sut.IsReconfigurationLocked());
|
||||
Assert.IsFalse(sut.RequestReconfigurationLock());
|
||||
});
|
||||
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
|
||||
result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
sut.ReleaseReconfigurationLock();
|
||||
});
|
||||
|
||||
Assert.IsFalse(sut.IsReconfigurationLocked());
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RequestReconfigurationLock_MustOnlyAllowLockingOnce()
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
Assert.IsFalse(sut.IsReconfigurationLocked());
|
||||
|
||||
var result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
var acquired = sut.RequestReconfigurationLock();
|
||||
|
||||
if (acquired)
|
||||
{
|
||||
Interlocked.Increment(ref count);
|
||||
}
|
||||
});
|
||||
|
||||
Assert.AreEqual(1, count);
|
||||
Assert.IsTrue(sut.IsReconfigurationLocked());
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void RequestSessionLock_MustOnlyAllowLockingOnce()
|
||||
{
|
||||
var count = 0;
|
||||
|
||||
Assert.IsFalse(sut.IsSessionLocked());
|
||||
|
||||
var result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
var acquired = sut.RequestSessionLock();
|
||||
|
||||
if (acquired)
|
||||
{
|
||||
Interlocked.Increment(ref count);
|
||||
}
|
||||
});
|
||||
|
||||
Assert.AreEqual(1, count);
|
||||
Assert.IsTrue(sut.IsSessionLocked());
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SessionLock_MustWorkCorrectly()
|
||||
{
|
||||
Assert.IsFalse(sut.IsSessionLocked());
|
||||
|
||||
sut.RequestSessionLock();
|
||||
|
||||
var result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
Assert.IsTrue(sut.IsSessionLocked());
|
||||
Assert.IsFalse(sut.RequestSessionLock());
|
||||
});
|
||||
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
|
||||
result = Parallel.For(1, 1000, (_) =>
|
||||
{
|
||||
sut.ReleaseSessionLock();
|
||||
});
|
||||
|
||||
Assert.IsFalse(sut.IsSessionLocked());
|
||||
Assert.IsTrue(result.IsCompleted);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,9 +64,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustNotInitializeBrowserIfNotEnabled()
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = true;
|
||||
settings.Browser.EnableBrowser = false;
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -79,17 +79,17 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustCorrectlyInitializeControls()
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = false;
|
||||
settings.Browser.EnableBrowser = true;
|
||||
settings.Taskbar.EnableTaskbar = false;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
actionCenter.Verify(a => a.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never);
|
||||
taskbar.Verify(t => t.AddApplicationControl(It.IsAny<IApplicationControl>(), true), Times.Never);
|
||||
|
||||
settings.ActionCenter.EnableActionCenter = true;
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
var order = 0;
|
||||
|
||||
context.Settings = new AppSettings();
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
displayMonitor.Setup(d => d.InitializePrimaryDisplay(It.IsAny<int>())).Callback(() => Assert.AreEqual(++order, 1));
|
||||
displayMonitor.Setup(d => d.StartMonitoringDisplayChanges()).Callback(() => Assert.AreEqual(++order, 2));
|
||||
|
@ -60,7 +60,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
var height = 25;
|
||||
|
||||
context.Settings = new AppSettings();
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
|
||||
sut.Perform();
|
||||
|
@ -75,7 +75,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
var height = 25;
|
||||
|
||||
context.Settings = new AppSettings();
|
||||
context.Settings.Taskbar.EnableTaskbar = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
|
||||
sut.Perform();
|
||||
|
|
|
@ -100,9 +100,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
context.Activators.Add(actionCenterActivator.Object);
|
||||
context.Activators.Add(taskviewActivator.Object);
|
||||
context.Activators.Add(terminationActivator.Object);
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.Keyboard.AllowAltTab = true;
|
||||
context.Settings.Security.AllowTermination = true;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -130,11 +130,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
context.Applications.Add(application1.Object);
|
||||
context.Applications.Add(application2.Object);
|
||||
context.Applications.Add(application3.Object);
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Applications.Whitelist.Add(application1Settings);
|
||||
context.Settings.Applications.Whitelist.Add(application2Settings);
|
||||
context.Settings.Applications.Whitelist.Add(application3Settings);
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -168,11 +168,11 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
context.Applications.Add(application1.Object);
|
||||
context.Applications.Add(application2.Object);
|
||||
context.Applications.Add(application3.Object);
|
||||
context.Settings.ActionCenter.EnableActionCenter = false;
|
||||
context.Settings.Taskbar.EnableTaskbar = false;
|
||||
context.Settings.Applications.Whitelist.Add(application1Settings);
|
||||
context.Settings.Applications.Whitelist.Add(application2Settings);
|
||||
context.Settings.Applications.Whitelist.Add(application3Settings);
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -211,10 +211,10 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustInitializeClock()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowClock = true;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowClock = true;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowClock = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowClock = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -225,10 +225,10 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustNotInitializeClock()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowClock = false;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowClock = false;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowClock = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowClock = false;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -239,9 +239,9 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustInitializeQuitButton()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Security.AllowTermination = false;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -265,12 +265,12 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustInitializeNotifications()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowApplicationInfo = true;
|
||||
context.Settings.ActionCenter.ShowApplicationLog = true;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowApplicationInfo = true;
|
||||
context.Settings.Taskbar.ShowApplicationLog = true;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowApplicationInfo = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowApplicationLog = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowApplicationInfo = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowApplicationLog = true;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -283,10 +283,10 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
{
|
||||
var logControl = new Mock<INotificationControl>();
|
||||
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowApplicationLog = false;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowApplicationLog = false;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowApplicationLog = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowApplicationLog = false;
|
||||
|
||||
uiFactory
|
||||
.Setup(f => f.CreateNotificationControl(It.IsAny<INotification>(), It.IsAny<Location>()))
|
||||
|
@ -301,14 +301,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustInitializeSystemComponents()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowAudio = true;
|
||||
context.Settings.ActionCenter.ShowKeyboardLayout = true;
|
||||
context.Settings.ActionCenter.ShowNetwork = true;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowAudio = true;
|
||||
context.Settings.Taskbar.ShowKeyboardLayout = true;
|
||||
context.Settings.Taskbar.ShowNetwork = true;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowAudio = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowKeyboardLayout = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowNetwork = true;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowAudio = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowKeyboardLayout = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowNetwork = true;
|
||||
|
||||
systemInfo.SetupGet(s => s.HasBattery).Returns(true);
|
||||
uiFactory.Setup(f => f.CreateAudioControl(It.IsAny<IAudio>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
|
||||
|
@ -329,14 +329,14 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustNotInitializeSystemComponents()
|
||||
{
|
||||
context.Settings.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.ActionCenter.ShowAudio = false;
|
||||
context.Settings.ActionCenter.ShowKeyboardLayout = false;
|
||||
context.Settings.ActionCenter.ShowNetwork = false;
|
||||
context.Settings.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.Taskbar.ShowAudio = false;
|
||||
context.Settings.Taskbar.ShowKeyboardLayout = false;
|
||||
context.Settings.Taskbar.ShowNetwork = false;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
context.Settings.UserInterface.ActionCenter.ShowAudio = false;
|
||||
context.Settings.UserInterface.ActionCenter.ShowKeyboardLayout = false;
|
||||
context.Settings.UserInterface.ActionCenter.ShowNetwork = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
context.Settings.UserInterface.Taskbar.ShowAudio = false;
|
||||
context.Settings.UserInterface.Taskbar.ShowKeyboardLayout = false;
|
||||
context.Settings.UserInterface.Taskbar.ShowNetwork = false;
|
||||
|
||||
systemInfo.SetupGet(s => s.HasBattery).Returns(false);
|
||||
uiFactory.Setup(f => f.CreateAudioControl(It.IsAny<IAudio>(), It.IsAny<Location>())).Returns(new Mock<ISystemControl>().Object);
|
||||
|
@ -360,7 +360,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
var actionCenterActivator = new Mock<IActionCenterActivator>();
|
||||
|
||||
context.Activators.Add(actionCenterActivator.Object);
|
||||
context.Settings.ActionCenter.EnableActionCenter = false;
|
||||
context.Settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
|
||||
sut.Perform();
|
||||
|
||||
|
@ -385,7 +385,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustNotInitializeTaskbarIfNotEnabled()
|
||||
{
|
||||
context.Settings.Taskbar.EnableTaskbar = false;
|
||||
context.Settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
sut.Perform();
|
||||
taskbar.VerifyNoOtherCalls();
|
||||
}
|
||||
|
|
|
@ -1,53 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Operations;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||
{
|
||||
[TestClass]
|
||||
public class SystemMonitorOperationTests
|
||||
{
|
||||
private ClientContext context;
|
||||
private Mock<ISystemMonitor> systemMonitor;
|
||||
private Mock<ILogger> logger;
|
||||
private SystemMonitorOperation sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
context = new ClientContext();
|
||||
systemMonitor = new Mock<ISystemMonitor>();
|
||||
logger = new Mock<ILogger>();
|
||||
|
||||
sut = new SystemMonitorOperation(context, systemMonitor.Object, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Perform_MustStartMonitor()
|
||||
{
|
||||
sut.Perform();
|
||||
|
||||
systemMonitor.Verify(s => s.Start(), Times.Once);
|
||||
systemMonitor.VerifyNoOtherCalls();
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Revert_MustStopMonitor()
|
||||
{
|
||||
sut.Revert();
|
||||
|
||||
systemMonitor.Verify(s => s.Stop(), Times.Once);
|
||||
systemMonitor.VerifyNoOtherCalls();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -163,9 +163,9 @@
|
|||
<Compile Include="Communication\ClientHostTests.cs" />
|
||||
<Compile Include="Notifications\AboutNotificationControllerTests.cs" />
|
||||
<Compile Include="Notifications\LogNotificationControllerTests.cs" />
|
||||
<Compile Include="Operations\SystemMonitorOperationTests.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="ClientControllerTests.cs" />
|
||||
<Compile Include="CoordinatorTests.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config">
|
||||
|
|
|
@ -20,7 +20,7 @@ namespace SafeExamBrowser.Client
|
|||
private const int ILMCM_LANGUAGEBAROFF = 0x00002;
|
||||
|
||||
private static readonly Mutex Mutex = new Mutex(true, AppConfig.CLIENT_MUTEX_NAME);
|
||||
private CompositionRoot instances = new CompositionRoot();
|
||||
private readonly CompositionRoot instances = new CompositionRoot();
|
||||
|
||||
[STAThread]
|
||||
public static void Main()
|
||||
|
|
|
@ -16,6 +16,7 @@ using System.Threading.Tasks;
|
|||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Operations.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
|
@ -30,12 +31,14 @@ using SafeExamBrowser.Logging.Contracts;
|
|||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Display;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
using SafeExamBrowser.Proctoring.Contracts;
|
||||
using SafeExamBrowser.Proctoring.Contracts.Events;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts.Data;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
@ -51,18 +54,19 @@ namespace SafeExamBrowser.Client
|
|||
private readonly IActionCenter actionCenter;
|
||||
private readonly IApplicationMonitor applicationMonitor;
|
||||
private readonly ClientContext context;
|
||||
private readonly ICoordinator coordinator;
|
||||
private readonly IDisplayMonitor displayMonitor;
|
||||
private readonly IExplorerShell explorerShell;
|
||||
private readonly IFileSystemDialog fileSystemDialog;
|
||||
private readonly IHashAlgorithm hashAlgorithm;
|
||||
private readonly ILogger logger;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly INetworkAdapter networkAdapter;
|
||||
private readonly IOperationSequence operations;
|
||||
private readonly IRegistry registry;
|
||||
private readonly IRuntimeProxy runtime;
|
||||
private readonly Action shutdown;
|
||||
private readonly ISplashScreen splashScreen;
|
||||
private readonly ISystemMonitor systemMonitor;
|
||||
private readonly ISystemSentinel sentinel;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
@ -75,24 +79,24 @@ namespace SafeExamBrowser.Client
|
|||
private AppSettings Settings => context.Settings;
|
||||
|
||||
private ILockScreen lockScreen;
|
||||
private bool sessionLocked;
|
||||
|
||||
internal ClientController(
|
||||
IActionCenter actionCenter,
|
||||
IApplicationMonitor applicationMonitor,
|
||||
ClientContext context,
|
||||
ICoordinator coordinator,
|
||||
IDisplayMonitor displayMonitor,
|
||||
IExplorerShell explorerShell,
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
INetworkAdapter networkAdapter,
|
||||
IOperationSequence operations,
|
||||
IRegistry registry,
|
||||
IRuntimeProxy runtime,
|
||||
Action shutdown,
|
||||
ISplashScreen splashScreen,
|
||||
ISystemMonitor systemMonitor,
|
||||
ISystemSentinel sentinel,
|
||||
ITaskbar taskbar,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
|
@ -100,18 +104,19 @@ namespace SafeExamBrowser.Client
|
|||
this.actionCenter = actionCenter;
|
||||
this.applicationMonitor = applicationMonitor;
|
||||
this.context = context;
|
||||
this.coordinator = coordinator;
|
||||
this.displayMonitor = displayMonitor;
|
||||
this.explorerShell = explorerShell;
|
||||
this.fileSystemDialog = fileSystemDialog;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.networkAdapter = networkAdapter;
|
||||
this.operations = operations;
|
||||
this.registry = registry;
|
||||
this.runtime = runtime;
|
||||
this.shutdown = shutdown;
|
||||
this.splashScreen = splashScreen;
|
||||
this.systemMonitor = systemMonitor;
|
||||
this.sentinel = sentinel;
|
||||
this.taskbar = taskbar;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
|
@ -136,6 +141,7 @@ namespace SafeExamBrowser.Client
|
|||
ShowShell();
|
||||
AutoStartApplications();
|
||||
ScheduleIntegrityVerification();
|
||||
StartMonitoring();
|
||||
|
||||
var communication = runtime.InformClientReady();
|
||||
|
||||
|
@ -174,7 +180,6 @@ namespace SafeExamBrowser.Client
|
|||
CloseShell();
|
||||
DeregisterEvents();
|
||||
UpdateSessionIntegrity();
|
||||
TerminateIntegrityVerification();
|
||||
|
||||
var success = operations.TryRevert() == OperationResult.Success;
|
||||
|
||||
|
@ -214,9 +219,12 @@ namespace SafeExamBrowser.Client
|
|||
ClientHost.ServerFailureActionRequested += ClientHost_ServerFailureActionRequested;
|
||||
ClientHost.Shutdown += ClientHost_Shutdown;
|
||||
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
|
||||
registry.ValueChanged += Registry_ValueChanged;
|
||||
networkAdapter.CredentialsRequired += NetworkAdapter_CredentialsRequired;
|
||||
runtime.ConnectionLost += Runtime_ConnectionLost;
|
||||
systemMonitor.SessionChanged += SystemMonitor_SessionChanged;
|
||||
sentinel.CursorChanged += Sentinel_CursorChanged;
|
||||
sentinel.EaseOfAccessChanged += Sentinel_EaseOfAccessChanged;
|
||||
sentinel.SessionChanged += Sentinel_SessionChanged;
|
||||
sentinel.StickyKeysChanged += Sentinel_StickyKeysChanged;
|
||||
taskbar.LoseFocusRequested += Taskbar_LoseFocusRequested;
|
||||
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
|
||||
|
||||
|
@ -239,9 +247,11 @@ namespace SafeExamBrowser.Client
|
|||
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
|
||||
applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed;
|
||||
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
|
||||
registry.ValueChanged -= Registry_ValueChanged;
|
||||
runtime.ConnectionLost -= Runtime_ConnectionLost;
|
||||
systemMonitor.SessionChanged -= SystemMonitor_SessionChanged;
|
||||
sentinel.CursorChanged -= Sentinel_CursorChanged;
|
||||
sentinel.EaseOfAccessChanged -= Sentinel_EaseOfAccessChanged;
|
||||
sentinel.SessionChanged -= Sentinel_SessionChanged;
|
||||
sentinel.StickyKeysChanged -= Sentinel_StickyKeysChanged;
|
||||
taskbar.LoseFocusRequested -= Taskbar_LoseFocusRequested;
|
||||
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
|
||||
|
||||
|
@ -279,12 +289,12 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
private void CloseShell()
|
||||
{
|
||||
if (Settings?.ActionCenter.EnableActionCenter == true)
|
||||
if (Settings?.UserInterface.ActionCenter.EnableActionCenter == true)
|
||||
{
|
||||
actionCenter.Close();
|
||||
}
|
||||
|
||||
if (Settings?.Taskbar.EnableTaskbar == true)
|
||||
if (Settings?.UserInterface.Taskbar.EnableTaskbar == true)
|
||||
{
|
||||
taskbar.Close();
|
||||
}
|
||||
|
@ -292,12 +302,12 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
private void ShowShell()
|
||||
{
|
||||
if (Settings.ActionCenter.EnableActionCenter)
|
||||
if (Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
actionCenter.Promote();
|
||||
}
|
||||
|
||||
if (Settings.Taskbar.EnableTaskbar)
|
||||
if (Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
taskbar.Show();
|
||||
}
|
||||
|
@ -324,6 +334,7 @@ namespace SafeExamBrowser.Client
|
|||
private void PrepareShutdown()
|
||||
{
|
||||
FinalizeProctoring();
|
||||
StopMonitoring();
|
||||
}
|
||||
|
||||
private void FinalizeProctoring()
|
||||
|
@ -355,28 +366,33 @@ namespace SafeExamBrowser.Client
|
|||
timer.Elapsed += (o, args) => VerifyApplicationIntegrity();
|
||||
timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void StartMonitoring()
|
||||
{
|
||||
sentinel.StartMonitoringSystemEvents();
|
||||
|
||||
if (!Settings.Security.AllowStickyKeys)
|
||||
{
|
||||
sentinel.StartMonitoringStickyKeys();
|
||||
}
|
||||
|
||||
if (Settings.Security.VerifyCursorConfiguration)
|
||||
{
|
||||
if (registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var names))
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
registry.StartMonitoring(RegistryValue.UserHive.Cursors_Key, name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to start monitoring cursor registry values!");
|
||||
}
|
||||
sentinel.StartMonitoringCursors();
|
||||
}
|
||||
|
||||
if (Settings.Service.IgnoreService)
|
||||
{
|
||||
registry.StartMonitoring(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name);
|
||||
sentinel.StartMonitoringEaseOfAccess();
|
||||
}
|
||||
}
|
||||
|
||||
private void StopMonitoring()
|
||||
{
|
||||
sentinel.StopMonitoring();
|
||||
}
|
||||
|
||||
private void VerifyApplicationIntegrity()
|
||||
{
|
||||
logger.Info($"Attempting to verify application integrity...");
|
||||
|
@ -403,7 +419,7 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||
|
||||
if (hasQuitPassword)
|
||||
if (hasQuitPassword && Settings.Security.VerifySessionIntegrity)
|
||||
{
|
||||
logger.Info($"Attempting to verify session integrity...");
|
||||
|
||||
|
@ -440,17 +456,12 @@ namespace SafeExamBrowser.Client
|
|||
}
|
||||
}
|
||||
|
||||
private void TerminateIntegrityVerification()
|
||||
{
|
||||
registry.StopMonitoring();
|
||||
}
|
||||
|
||||
private void ApplicationMonitor_ExplorerStarted()
|
||||
{
|
||||
logger.Info("Trying to terminate Windows explorer...");
|
||||
explorerShell.Terminate();
|
||||
logger.Info("Re-initializing working area...");
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
logger.Info("Re-initializing shell...");
|
||||
actionCenter.InitializeBounds();
|
||||
taskbar.InitializeBounds();
|
||||
|
@ -481,6 +492,36 @@ namespace SafeExamBrowser.Client
|
|||
}
|
||||
|
||||
private void Browser_ConfigurationDownloadRequested(string fileName, DownloadEventArgs args)
|
||||
{
|
||||
args.AllowDownload = false;
|
||||
|
||||
if (IsAllowedToReconfigure(args.Url))
|
||||
{
|
||||
if (coordinator.RequestReconfigurationLock())
|
||||
{
|
||||
args.AllowDownload = true;
|
||||
args.Callback = Browser_ConfigurationDownloadFinished;
|
||||
args.DownloadPath = Path.Combine(context.AppConfig.TemporaryDirectory, fileName);
|
||||
|
||||
splashScreen.Show();
|
||||
splashScreen.BringToForeground();
|
||||
splashScreen.SetIndeterminate();
|
||||
splashScreen.UpdateStatus(TextKey.OperationStatus_InitializeSession, true);
|
||||
|
||||
logger.Info($"Allowed download request for configuration file '{fileName}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"A reconfiguration is already in progress, denied download request for configuration file '{fileName}'!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info($"Reconfiguration is not allowed, denied download request for configuration file '{fileName}'.");
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsAllowedToReconfigure(string url)
|
||||
{
|
||||
var allow = false;
|
||||
var hasQuitPassword = !string.IsNullOrWhiteSpace(Settings.Security.QuitPasswordHash);
|
||||
|
@ -492,9 +533,9 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
var expression = Regex.Escape(Settings.Security.ReconfigurationUrl).Replace(@"\*", ".*");
|
||||
var regex = new Regex($"^{expression}$", RegexOptions.IgnoreCase);
|
||||
var sebUrl = args.Url.Replace(Uri.UriSchemeHttps, context.AppConfig.SebUriSchemeSecure).Replace(Uri.UriSchemeHttp, context.AppConfig.SebUriScheme);
|
||||
var sebUrl = url.Replace(Uri.UriSchemeHttps, context.AppConfig.SebUriSchemeSecure).Replace(Uri.UriSchemeHttp, context.AppConfig.SebUriScheme);
|
||||
|
||||
allow = Settings.Security.AllowReconfiguration && (regex.IsMatch(args.Url) || regex.IsMatch(sebUrl));
|
||||
allow = Settings.Security.AllowReconfiguration && (regex.IsMatch(url) || regex.IsMatch(sebUrl));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -506,24 +547,7 @@ namespace SafeExamBrowser.Client
|
|||
allow = Settings.ConfigurationMode == ConfigurationMode.ConfigureClient || Settings.Security.AllowReconfiguration;
|
||||
}
|
||||
|
||||
if (allow)
|
||||
{
|
||||
args.AllowDownload = true;
|
||||
args.Callback = Browser_ConfigurationDownloadFinished;
|
||||
args.DownloadPath = Path.Combine(context.AppConfig.TemporaryDirectory, fileName);
|
||||
|
||||
splashScreen.Show();
|
||||
splashScreen.BringToForeground();
|
||||
splashScreen.SetIndeterminate();
|
||||
splashScreen.UpdateStatus(TextKey.OperationStatus_InitializeSession, true);
|
||||
|
||||
logger.Info($"Allowed download request for configuration file '{fileName}'.");
|
||||
}
|
||||
else
|
||||
{
|
||||
args.AllowDownload = false;
|
||||
logger.Info($"Denied download request for configuration file '{fileName}'.");
|
||||
}
|
||||
return allow;
|
||||
}
|
||||
|
||||
private void Browser_ConfigurationDownloadFinished(bool success, string url, string filePath = null)
|
||||
|
@ -541,15 +565,19 @@ namespace SafeExamBrowser.Client
|
|||
else
|
||||
{
|
||||
logger.Error($"Failed to communicate reconfiguration request for '{filePath}'!");
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ReconfigurationError, TextKey.MessageBox_ReconfigurationErrorTitle, icon: MessageBoxIcon.Error, parent: splashScreen);
|
||||
splashScreen.Hide();
|
||||
coordinator.ReleaseReconfigurationLock();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to download configuration file '{filePath}'!");
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ConfigurationDownloadError, TextKey.MessageBox_ConfigurationDownloadErrorTitle, icon: MessageBoxIcon.Error, parent: splashScreen);
|
||||
splashScreen.Hide();
|
||||
coordinator.ReleaseReconfigurationLock();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -637,6 +665,7 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
logger.Info("The reconfiguration was aborted by the runtime.");
|
||||
splashScreen.Hide();
|
||||
coordinator.ReleaseReconfigurationLock();
|
||||
}
|
||||
|
||||
private void ClientHost_ReconfigurationDenied(ReconfigurationEventArgs args)
|
||||
|
@ -644,6 +673,7 @@ namespace SafeExamBrowser.Client
|
|||
logger.Info($"The reconfiguration request for '{args.ConfigurationPath}' was denied by the runtime!");
|
||||
messageBox.Show(TextKey.MessageBox_ReconfigurationDenied, TextKey.MessageBox_ReconfigurationDeniedTitle, parent: splashScreen);
|
||||
splashScreen.Hide();
|
||||
coordinator.ReleaseReconfigurationLock();
|
||||
}
|
||||
|
||||
private void ClientHost_ServerFailureActionRequested(ServerFailureActionRequestEventArgs args)
|
||||
|
@ -665,7 +695,7 @@ namespace SafeExamBrowser.Client
|
|||
private void DisplayMonitor_DisplaySettingsChanged()
|
||||
{
|
||||
logger.Info("Re-initializing working area...");
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
|
||||
logger.Info("Re-initializing shell...");
|
||||
actionCenter.InitializeBounds();
|
||||
|
@ -690,6 +720,18 @@ namespace SafeExamBrowser.Client
|
|||
}
|
||||
}
|
||||
|
||||
private void NetworkAdapter_CredentialsRequired(CredentialsRequiredEventArgs args)
|
||||
{
|
||||
var message = text.Get(TextKey.CredentialsDialog_WirelessNetworkMessage).Replace("%%_NAME_%%", args.NetworkName);
|
||||
var title = text.Get(TextKey.CredentialsDialog_WirelessNetworkTitle);
|
||||
var dialog = uiFactory.CreateCredentialsDialog(CredentialsDialogPurpose.WirelessNetwork, message, title);
|
||||
var result = dialog.Show();
|
||||
|
||||
args.Password = result.Password;
|
||||
args.Success = result.Success;
|
||||
args.Username = result.Username;
|
||||
}
|
||||
|
||||
private void Operations_ActionRequired(ActionRequiredEventArgs args)
|
||||
{
|
||||
switch (args)
|
||||
|
@ -742,31 +784,25 @@ namespace SafeExamBrowser.Client
|
|||
splashScreen.UpdateStatus(status, true);
|
||||
}
|
||||
|
||||
private void Registry_ValueChanged(string key, string name, object oldValue, object newValue)
|
||||
private void Runtime_ConnectionLost()
|
||||
{
|
||||
if (key == RegistryValue.UserHive.Cursors_Key)
|
||||
{
|
||||
HandleCursorRegistryChange(key, name, oldValue, newValue);
|
||||
}
|
||||
else if (key == RegistryValue.MachineHive.EaseOfAccess_Key)
|
||||
{
|
||||
HandleEaseOfAccessRegistryChange(key, name, oldValue, newValue);
|
||||
}
|
||||
logger.Error("Lost connection to the runtime!");
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
|
||||
shutdown.Invoke();
|
||||
}
|
||||
|
||||
private void HandleCursorRegistryChange(string key, string name, object oldValue, object newValue)
|
||||
private void Sentinel_CursorChanged(SentinelEventArgs args)
|
||||
{
|
||||
logger.Warn($@"The cursor registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen...");
|
||||
|
||||
if (!sessionLocked)
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
var message = text.Get(TextKey.LockScreen_CursorMessage);
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorContinueOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_CursorTerminateOption) };
|
||||
|
||||
sessionLocked = true;
|
||||
registry.StopMonitoring(key, name);
|
||||
args.Allow = true;
|
||||
logger.Info("Cursor changed! Attempting to show lock screen...");
|
||||
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
|
@ -780,27 +816,25 @@ namespace SafeExamBrowser.Client
|
|||
TryRequestShutdown();
|
||||
}
|
||||
|
||||
sessionLocked = false;
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Lock screen is already active.");
|
||||
logger.Info("Cursor changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleEaseOfAccessRegistryChange(string key, string name, object oldValue, object newValue)
|
||||
private void Sentinel_EaseOfAccessChanged(SentinelEventArgs args)
|
||||
{
|
||||
logger.Warn($@"The ease of access registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'! Attempting to show lock screen...");
|
||||
|
||||
if (!sessionLocked)
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
var message = text.Get(TextKey.LockScreen_EaseOfAccessMessage);
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_EaseOfAccessContinueOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_EaseOfAccessTerminateOption) };
|
||||
|
||||
sessionLocked = true;
|
||||
registry.StopMonitoring(key, name);
|
||||
args.Allow = true;
|
||||
logger.Info("Ease of access changed! Attempting to show lock screen...");
|
||||
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
|
@ -814,20 +848,85 @@ namespace SafeExamBrowser.Client
|
|||
TryRequestShutdown();
|
||||
}
|
||||
|
||||
sessionLocked = false;
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Lock screen is already active.");
|
||||
logger.Info("Ease of access changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Runtime_ConnectionLost()
|
||||
private void Sentinel_SessionChanged()
|
||||
{
|
||||
logger.Error("Lost connection to the runtime!");
|
||||
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
|
||||
var allow = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
|
||||
var disable = Settings.Security.DisableSessionChangeLockScreen;
|
||||
|
||||
shutdown.Invoke();
|
||||
if (allow || disable)
|
||||
{
|
||||
logger.Info($"Detected user session change, but {(allow ? "session locking and/or switching is allowed" : "lock screen is deactivated")}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = text.Get(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...");
|
||||
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
if (result.OptionId == continueOption.Id)
|
||||
{
|
||||
logger.Info("The session will be allowed to resume as requested by the user...");
|
||||
}
|
||||
else if (result.OptionId == terminateOption.Id)
|
||||
{
|
||||
logger.Info("Attempting to shutdown as requested by the user...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("User session changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Sentinel_StickyKeysChanged(SentinelEventArgs args)
|
||||
{
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
var message = text.Get(TextKey.LockScreen_StickyKeysMessage);
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_StickyKeysContinueOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_StickyKeysTerminateOption) };
|
||||
|
||||
args.Allow = true;
|
||||
logger.Info("Sticky keys changed! Attempting to show lock screen...");
|
||||
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
if (result.OptionId == continueOption.Id)
|
||||
{
|
||||
logger.Info("The session will be allowed to resume as requested by the user...");
|
||||
}
|
||||
else if (result.OptionId == terminateOption.Id)
|
||||
{
|
||||
logger.Info("Attempting to shutdown as requested by the user...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Sticky keys changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Server_LockScreenConfirmed()
|
||||
|
@ -840,11 +939,10 @@ namespace SafeExamBrowser.Client
|
|||
{
|
||||
logger.Info("Attempting to show lock screen as requested by the server...");
|
||||
|
||||
if (!sessionLocked)
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
sessionLocked = true;
|
||||
ShowLockScreen(message, text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||
sessionLocked = false;
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -865,44 +963,6 @@ namespace SafeExamBrowser.Client
|
|||
ResumeActivators();
|
||||
}
|
||||
|
||||
private void SystemMonitor_SessionChanged()
|
||||
{
|
||||
var allow = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
|
||||
var disable = Settings.Security.DisableSessionChangeLockScreen;
|
||||
var message = text.Get(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) };
|
||||
|
||||
if (allow || disable)
|
||||
{
|
||||
logger.Info($"Detected user session change, but {(allow ? "session locking and/or switching is allowed" : "lock screen is deactivated")}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Detected user session change!");
|
||||
|
||||
if (!sessionLocked)
|
||||
{
|
||||
sessionLocked = true;
|
||||
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
if (result.OptionId == terminateOption.Id)
|
||||
{
|
||||
logger.Info("Attempting to shutdown as requested by the user...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
|
||||
sessionLocked = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Lock screen is already active.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Taskbar_LoseFocusRequested(bool forward)
|
||||
{
|
||||
Browser.Focus(forward);
|
||||
|
@ -990,7 +1050,7 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
logger.Info("Showing lock screen...");
|
||||
PauseActivators();
|
||||
lockScreen = uiFactory.CreateLockScreen(message, title, options);
|
||||
lockScreen = uiFactory.CreateLockScreen(message, title, options, Settings.UserInterface.LockScreen);
|
||||
lockScreen.Show();
|
||||
|
||||
if (Settings.SessionMode == SessionMode.Server)
|
||||
|
|
|
@ -114,12 +114,13 @@ namespace SafeExamBrowser.Client
|
|||
|
||||
var applicationFactory = new ApplicationFactory(applicationMonitor, ModuleLogger(nameof(ApplicationFactory)), nativeMethods, processFactory, new Registry(ModuleLogger(nameof(Registry))));
|
||||
var clipboard = new Clipboard(ModuleLogger(nameof(Clipboard)), nativeMethods);
|
||||
var coordinator = new Coordinator();
|
||||
var displayMonitor = new DisplayMonitor(ModuleLogger(nameof(DisplayMonitor)), nativeMethods, systemInfo);
|
||||
var explorerShell = new ExplorerShell(ModuleLogger(nameof(ExplorerShell)), nativeMethods);
|
||||
var fileSystemDialog = BuildFileSystemDialog();
|
||||
var hashAlgorithm = new HashAlgorithm();
|
||||
var sentinel = new SystemSentinel(ModuleLogger(nameof(SystemSentinel)), nativeMethods, registry);
|
||||
var splashScreen = uiFactory.CreateSplashScreen();
|
||||
var systemMonitor = new SystemMonitor(ModuleLogger(nameof(SystemMonitor)));
|
||||
|
||||
var operations = new Queue<IOperation>();
|
||||
|
||||
|
@ -135,7 +136,6 @@ namespace SafeExamBrowser.Client
|
|||
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
|
||||
operations.Enqueue(new ApplicationOperation(context, applicationFactory, applicationMonitor, logger, text));
|
||||
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
|
||||
operations.Enqueue(new SystemMonitorOperation(context, systemMonitor, logger));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildBrowserOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildServerOperation));
|
||||
|
@ -148,18 +148,19 @@ namespace SafeExamBrowser.Client
|
|||
actionCenter,
|
||||
applicationMonitor,
|
||||
context,
|
||||
coordinator,
|
||||
displayMonitor,
|
||||
explorerShell,
|
||||
fileSystemDialog,
|
||||
hashAlgorithm,
|
||||
logger,
|
||||
messageBox,
|
||||
networkAdapter,
|
||||
sequence,
|
||||
registry,
|
||||
runtimeProxy,
|
||||
shutdown,
|
||||
splashScreen,
|
||||
systemMonitor,
|
||||
sentinel,
|
||||
taskbar,
|
||||
text,
|
||||
uiFactory);
|
||||
|
|
46
SafeExamBrowser.Client/Contracts/ICoordinator.cs
Normal file
46
SafeExamBrowser.Client/Contracts/ICoordinator.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Client.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Coordinates concurrent operations of the client application.
|
||||
/// </summary>
|
||||
internal interface ICoordinator
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether the reconfiguration lock is currently occupied.
|
||||
/// </summary>
|
||||
bool IsReconfigurationLocked();
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the session lock is currently occupied.
|
||||
/// </summary>
|
||||
bool IsSessionLocked();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the reconfiguration lock.
|
||||
/// </summary>
|
||||
void ReleaseReconfigurationLock();
|
||||
|
||||
/// <summary>
|
||||
/// Releases the session lock.
|
||||
/// </summary>
|
||||
void ReleaseSessionLock();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to acquire the unique reconfiguration lock. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RequestReconfigurationLock();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to acquire the unique session lock. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RequestSessionLock();
|
||||
}
|
||||
}
|
78
SafeExamBrowser.Client/Coordinator.cs
Normal file
78
SafeExamBrowser.Client/Coordinator.cs
Normal file
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Concurrent;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client
|
||||
{
|
||||
internal class Coordinator : ICoordinator
|
||||
{
|
||||
private readonly ConcurrentBag<Guid> reconfiguration;
|
||||
private readonly ConcurrentBag<Guid> session;
|
||||
|
||||
internal Coordinator()
|
||||
{
|
||||
reconfiguration = new ConcurrentBag<Guid>();
|
||||
session = new ConcurrentBag<Guid>();
|
||||
}
|
||||
|
||||
public bool IsReconfigurationLocked()
|
||||
{
|
||||
return !reconfiguration.IsEmpty;
|
||||
}
|
||||
|
||||
public bool IsSessionLocked()
|
||||
{
|
||||
return !session.IsEmpty;
|
||||
}
|
||||
|
||||
public void ReleaseReconfigurationLock()
|
||||
{
|
||||
reconfiguration.TryTake(out _);
|
||||
}
|
||||
|
||||
public void ReleaseSessionLock()
|
||||
{
|
||||
session.TryTake(out _);
|
||||
}
|
||||
|
||||
public bool RequestReconfigurationLock()
|
||||
{
|
||||
var acquired = false;
|
||||
|
||||
lock (reconfiguration)
|
||||
{
|
||||
if (reconfiguration.IsEmpty)
|
||||
{
|
||||
reconfiguration.Add(Guid.NewGuid());
|
||||
acquired = true;
|
||||
}
|
||||
}
|
||||
|
||||
return acquired;
|
||||
}
|
||||
|
||||
public bool RequestSessionLock()
|
||||
{
|
||||
var acquired = false;
|
||||
|
||||
lock (session)
|
||||
{
|
||||
if (session.IsEmpty)
|
||||
{
|
||||
session.Add(Guid.NewGuid());
|
||||
acquired = true;
|
||||
}
|
||||
}
|
||||
|
||||
return acquired;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -50,12 +50,12 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
Context.Browser.Initialize();
|
||||
|
||||
if (Context.Settings.ActionCenter.EnableActionCenter)
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
actionCenter.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.ActionCenter), true);
|
||||
}
|
||||
|
||||
if (Context.Settings.Taskbar.EnableTaskbar)
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
taskbar.AddApplicationControl(uiFactory.CreateApplicationControl(Context.Browser, Location.Taskbar), true);
|
||||
}
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
logger.Info("Initializing working area...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWorkingArea);
|
||||
|
||||
displayMonitor.InitializePrimaryDisplay(Context.Settings.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
displayMonitor.InitializePrimaryDisplay(Context.Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
displayMonitor.StartMonitoringDisplayChanges();
|
||||
|
||||
return OperationResult.Success;
|
||||
|
|
|
@ -107,7 +107,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
{
|
||||
foreach (var activator in Context.Activators)
|
||||
{
|
||||
if (Context.Settings.ActionCenter.EnableActionCenter && activator is IActionCenterActivator actionCenterActivator)
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter && activator is IActionCenterActivator actionCenterActivator)
|
||||
{
|
||||
actionCenter.Register(actionCenterActivator);
|
||||
actionCenterActivator.Start();
|
||||
|
@ -124,7 +124,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
terminationActivator.Start();
|
||||
}
|
||||
|
||||
if (Context.Settings.Taskbar.EnableTaskbar && activator is ITaskbarActivator taskbarActivator)
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar && activator is ITaskbarActivator taskbarActivator)
|
||||
{
|
||||
taskbar.Register(taskbarActivator);
|
||||
taskbarActivator.Start();
|
||||
|
@ -134,7 +134,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.EnableActionCenter)
|
||||
if (Context.Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
logger.Info("Initializing action center...");
|
||||
actionCenter.InitializeText(text);
|
||||
|
@ -168,7 +168,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.EnableTaskbar)
|
||||
if (Context.Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
logger.Info("Initializing taskbar...");
|
||||
taskbar.InitializeText(text);
|
||||
|
@ -232,7 +232,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeAboutNotificationForActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.ShowApplicationInfo)
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowApplicationInfo)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(aboutNotification, Location.ActionCenter));
|
||||
}
|
||||
|
@ -240,7 +240,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeAboutNotificationForTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.ShowApplicationInfo)
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowApplicationInfo)
|
||||
{
|
||||
taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(aboutNotification, Location.Taskbar));
|
||||
}
|
||||
|
@ -248,7 +248,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeAudioForActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.ShowAudio)
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowAudio)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateAudioControl(audio, Location.ActionCenter));
|
||||
}
|
||||
|
@ -256,7 +256,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeAudioForTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.ShowAudio)
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowAudio)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateAudioControl(audio, Location.Taskbar));
|
||||
}
|
||||
|
@ -264,17 +264,17 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeClockForActionCenter()
|
||||
{
|
||||
actionCenter.ShowClock = Context.Settings.ActionCenter.ShowClock;
|
||||
actionCenter.ShowClock = Context.Settings.UserInterface.ActionCenter.ShowClock;
|
||||
}
|
||||
|
||||
private void InitializeClockForTaskbar()
|
||||
{
|
||||
taskbar.ShowClock = Context.Settings.Taskbar.ShowClock;
|
||||
taskbar.ShowClock = Context.Settings.UserInterface.Taskbar.ShowClock;
|
||||
}
|
||||
|
||||
private void InitializeLogNotificationForActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.ShowApplicationLog)
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowApplicationLog)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(logNotification, Location.ActionCenter));
|
||||
}
|
||||
|
@ -282,7 +282,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeLogNotificationForTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.ShowApplicationLog)
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowApplicationLog)
|
||||
{
|
||||
taskbar.AddNotificationControl(uiFactory.CreateNotificationControl(logNotification, Location.Taskbar));
|
||||
}
|
||||
|
@ -290,7 +290,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeKeyboardLayoutForActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.ShowKeyboardLayout)
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowKeyboardLayout)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateKeyboardLayoutControl(keyboard, Location.ActionCenter));
|
||||
}
|
||||
|
@ -298,7 +298,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeKeyboardLayoutForTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.ShowKeyboardLayout)
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowKeyboardLayout)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateKeyboardLayoutControl(keyboard, Location.Taskbar));
|
||||
}
|
||||
|
@ -332,7 +332,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeNetworkForActionCenter()
|
||||
{
|
||||
if (Context.Settings.ActionCenter.ShowNetwork)
|
||||
if (Context.Settings.UserInterface.ActionCenter.ShowNetwork)
|
||||
{
|
||||
actionCenter.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.ActionCenter));
|
||||
}
|
||||
|
@ -340,7 +340,7 @@ namespace SafeExamBrowser.Client.Operations
|
|||
|
||||
private void InitializeNetworkForTaskbar()
|
||||
{
|
||||
if (Context.Settings.Taskbar.ShowNetwork)
|
||||
if (Context.Settings.UserInterface.Taskbar.ShowNetwork)
|
||||
{
|
||||
taskbar.AddSystemControl(uiFactory.CreateNetworkControl(networkAdapter, Location.Taskbar));
|
||||
}
|
||||
|
|
|
@ -1,51 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Monitoring.Contracts.System;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class SystemMonitorOperation : ClientOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly ISystemMonitor systemMonitor;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public SystemMonitorOperation(ClientContext context, ISystemMonitor systemMonitor, ILogger logger) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.systemMonitor = systemMonitor;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing system events...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeSystemEvents);
|
||||
|
||||
systemMonitor.Start();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
logger.Info("Finalizing system events...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeSystemEvents);
|
||||
|
||||
systemMonitor.Stop();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -16,6 +16,9 @@ using System.Windows;
|
|||
// to COM components. If you need to access a type in this assembly from
|
||||
// COM, set the ComVisible attribute to true on that type.
|
||||
[assembly: ComVisible(false)]
|
||||
|
||||
// Required for mocking internal contracts with Moq
|
||||
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")]
|
||||
[assembly: InternalsVisibleTo("SafeExamBrowser.Client.UnitTests")]
|
||||
|
||||
//In order to begin building localizable applications, set
|
||||
|
|
|
@ -74,6 +74,8 @@
|
|||
<Compile Include="App.cs" />
|
||||
<Compile Include="ClientContext.cs" />
|
||||
<Compile Include="ClientController.cs" />
|
||||
<Compile Include="Contracts\ICoordinator.cs" />
|
||||
<Compile Include="Coordinator.cs" />
|
||||
<Compile Include="Operations\ClientHostDisconnectionOperation.cs" />
|
||||
<Compile Include="Operations\ClientOperation.cs" />
|
||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||
|
@ -95,7 +97,6 @@
|
|||
<Compile Include="Operations\ApplicationOperation.cs" />
|
||||
<Compile Include="Operations\ServerOperation.cs" />
|
||||
<Compile Include="Operations\ShellOperation.cs" />
|
||||
<Compile Include="Operations\SystemMonitorOperation.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
|
|
@ -26,6 +26,11 @@ namespace SafeExamBrowser.Configuration.Contracts
|
|||
/// </summary>
|
||||
public Guid ClientAuthenticationToken { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether a configuration resource needs to be loaded in the browser because it requires authentication or is a webpage.
|
||||
/// </summary>
|
||||
public bool IsBrowserResource { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The unique session identifier.
|
||||
/// </summary>
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Threading.Tasks;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||
using SafeExamBrowser.Configuration.Cryptography;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.UnitTests.Cryptography
|
||||
{
|
||||
[TestClass]
|
||||
public class KeyGeneratorTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IIntegrityModule> integrityModule;
|
||||
private Mock<ILogger> logger;
|
||||
private KeyGenerator sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
integrityModule = new Mock<IIntegrityModule>();
|
||||
logger = new Mock<ILogger>();
|
||||
|
||||
sut = new KeyGenerator(appConfig, integrityModule.Object, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
|
||||
public void CalculateBrowserExamKeyHash_MustFailWithoutUrl()
|
||||
{
|
||||
sut.CalculateBrowserExamKeyHash(default, default, default);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(Exception), AllowDerivedTypes = true)]
|
||||
public void CalculateConfigurationKeyHash_MustFailWithoutUrl()
|
||||
{
|
||||
sut.CalculateConfigurationKeyHash(default, default);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustAllowForConcurrentKeyHashCalculation()
|
||||
{
|
||||
Parallel.For(0, 1000, (_) =>
|
||||
{
|
||||
sut.CalculateBrowserExamKeyHash(default, default, "https://www.safeexambrowser.org");
|
||||
sut.CalculateConfigurationKeyHash(default, "https://www.safeexambrowser.org");
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -148,6 +148,7 @@
|
|||
<Compile Include="Cryptography\PasswordEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\PublicKeySymmetricEncryptionTests.cs" />
|
||||
<Compile Include="Cryptography\KeyGeneratorTests.cs" />
|
||||
<Compile Include="DataCompression\GZipCompressorTests.cs" />
|
||||
<Compile Include="DataFormats\BinaryParserTests.cs" />
|
||||
<Compile Include="DataFormats\BinarySerializerTests.cs" />
|
||||
|
|
|
@ -31,7 +31,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
new UserInterfaceDataMapper()
|
||||
};
|
||||
|
||||
internal void MapRawDataToSettings(IDictionary<string, object> rawData, AppSettings settings)
|
||||
internal void Map(IDictionary<string, object> rawData, AppSettings settings)
|
||||
{
|
||||
foreach (var item in rawData)
|
||||
{
|
||||
|
|
|
@ -575,12 +575,12 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
var useCustomForDesktop = rawData.TryGetValue(Keys.Browser.UserAgentModeDesktop, out var value) && value as int? != DEFAULT;
|
||||
var useCustomForMobile = rawData.TryGetValue(Keys.Browser.UserAgentModeMobile, out value) && value as int? != DEFAULT;
|
||||
|
||||
if (settings.UserInterfaceMode == UserInterfaceMode.Desktop && useCustomForDesktop)
|
||||
if (settings.UserInterface.Mode == UserInterfaceMode.Desktop && useCustomForDesktop)
|
||||
{
|
||||
settings.Browser.UseCustomUserAgent = true;
|
||||
settings.Browser.CustomUserAgent = rawData[Keys.Browser.CustomUserAgentDesktop] as string;
|
||||
}
|
||||
else if (settings.UserInterfaceMode == UserInterfaceMode.Mobile && useCustomForMobile)
|
||||
else if (settings.UserInterface.Mode == UserInterfaceMode.Mobile && useCustomForMobile)
|
||||
{
|
||||
settings.Browser.UseCustomUserAgent = true;
|
||||
settings.Browser.CustomUserAgent = rawData[Keys.Browser.CustomUserAgentMobile] as string;
|
||||
|
|
|
@ -21,60 +21,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.Proctoring.ForceRaiseHandMessage:
|
||||
MapForceRaiseHandMessage(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AllowChat:
|
||||
MapJitsiMeetAllowChat(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AllowClosedCaptions:
|
||||
MapJitsiMeetAllowClosedCaptions(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AllowRaiseHand:
|
||||
MapJitsiMeetAllowRaiseHands(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AllowRecording:
|
||||
MapJitsiMeetAllowRecording(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AllowTileView:
|
||||
MapJitsiMeetAllowTileView(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AudioMuted:
|
||||
MapJitsiMeetAudioMuted(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.AudioOnly:
|
||||
MapJitsiMeetAudioOnly(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.Enabled:
|
||||
MapJitsiMeetEnabled(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.ReceiveAudio:
|
||||
MapJitsiMeetReceiveAudio(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.ReceiveVideo:
|
||||
MapJitsiMeetReceiveVideo(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.RoomName:
|
||||
MapJitsiMeetRoomName(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.SendAudio:
|
||||
MapJitsiMeetSendAudio(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.SendVideo:
|
||||
MapJitsiMeetSendVideo(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.ServerUrl:
|
||||
MapJitsiMeetServerUrl(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.ShowMeetingName:
|
||||
MapJitsiMeetShowMeetingName(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.Subject:
|
||||
MapJitsiMeetSubject(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.Token:
|
||||
MapJitsiMeetToken(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.JitsiMeet.VideoMuted:
|
||||
MapJitsiMeetVideoMuted(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.ScreenProctoring.ClientId:
|
||||
MapClientId(settings, value);
|
||||
break;
|
||||
|
@ -120,51 +66,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.Proctoring.ShowTaskbarNotification:
|
||||
MapShowTaskbarNotification(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.WindowVisibility:
|
||||
MapWindowVisibility(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.AllowChat:
|
||||
MapZoomAllowChat(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.AllowClosedCaptions:
|
||||
MapZoomAllowClosedCaptions(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.AllowRaiseHand:
|
||||
MapZoomAllowRaiseHands(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.AudioMuted:
|
||||
MapZoomAudioMuted(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.Enabled:
|
||||
MapZoomEnabled(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.MeetingNumber:
|
||||
MapZoomMeetingNumber(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.ReceiveAudio:
|
||||
MapZoomReceiveAudio(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.ReceiveVideo:
|
||||
MapZoomReceiveVideo(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.SendAudio:
|
||||
MapZoomSendAudio(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.SendVideo:
|
||||
MapZoomSendVideo(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.Signature:
|
||||
MapZoomSignature(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.Subject:
|
||||
MapZoomSubject(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.UserName:
|
||||
MapZoomUserName(settings, value);
|
||||
break;
|
||||
case Keys.Proctoring.Zoom.VideoMuted:
|
||||
MapZoomVideoMuted(settings, value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -176,150 +77,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAllowChat(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AllowChat = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAllowClosedCaptions(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AllowClosedCaptions = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAllowRaiseHands(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AllowRaiseHand = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAllowRecording(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AllowRecording = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAllowTileView(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AllowTileView = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAudioMuted(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool audioMuted)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AudioMuted = audioMuted;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetAudioOnly(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool audioOnly)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.AudioOnly = audioOnly;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetEnabled(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool enabled)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetReceiveAudio(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool receive)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.ReceiveAudio = receive;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetReceiveVideo(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool receive)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.ReceiveVideo = receive;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetRoomName(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string name)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.RoomName = name;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetSendAudio(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool send)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.SendAudio = send;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetSendVideo(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool send)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.SendVideo = send;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetServerUrl(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string url)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.ServerUrl = url;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetShowMeetingName(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.ShowMeetingName = show;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetSubject(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string subject)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.Subject = subject;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetToken(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string token)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.Token = token;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapJitsiMeetVideoMuted(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool muted)
|
||||
{
|
||||
settings.Proctoring.JitsiMeet.VideoMuted = muted;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapCaptureApplicationData(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool capture)
|
||||
|
@ -463,144 +220,5 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
settings.Proctoring.ShowTaskbarNotification = show;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapWindowVisibility(AppSettings settings, object value)
|
||||
{
|
||||
const int HIDDEN = 0;
|
||||
const int ALLOW_SHOW = 1;
|
||||
const int ALLOW_HIDE = 2;
|
||||
const int VISIBLE = 3;
|
||||
|
||||
if (value is int visibility)
|
||||
{
|
||||
switch (visibility)
|
||||
{
|
||||
case HIDDEN:
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
|
||||
break;
|
||||
case ALLOW_SHOW:
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.AllowToShow;
|
||||
break;
|
||||
case ALLOW_HIDE:
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.AllowToHide;
|
||||
break;
|
||||
case VISIBLE:
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.Visible;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomAllowChat(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.Zoom.AllowChat = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomAllowClosedCaptions(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.Zoom.AllowClosedCaptions = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomAllowRaiseHands(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Proctoring.Zoom.AllowRaiseHand = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomAudioMuted(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool muted)
|
||||
{
|
||||
settings.Proctoring.Zoom.AudioMuted = muted;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomEnabled(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool enabled)
|
||||
{
|
||||
settings.Proctoring.Zoom.Enabled = enabled;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomMeetingNumber(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string number)
|
||||
{
|
||||
settings.Proctoring.Zoom.MeetingNumber = number;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomReceiveAudio(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool receive)
|
||||
{
|
||||
settings.Proctoring.Zoom.ReceiveAudio = receive;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomReceiveVideo(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool receive)
|
||||
{
|
||||
settings.Proctoring.Zoom.ReceiveVideo = receive;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomSendAudio(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool send)
|
||||
{
|
||||
settings.Proctoring.Zoom.SendAudio = send;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomSendVideo(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool send)
|
||||
{
|
||||
settings.Proctoring.Zoom.SendVideo = send;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomSignature(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string signature)
|
||||
{
|
||||
settings.Proctoring.Zoom.Signature = signature;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomSubject(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string subject)
|
||||
{
|
||||
settings.Proctoring.Zoom.Subject = subject;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomUserName(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string name)
|
||||
{
|
||||
settings.Proctoring.Zoom.UserName = name;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapZoomVideoMuted(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool muted)
|
||||
{
|
||||
settings.Proctoring.Zoom.VideoMuted = muted;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,6 +26,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.Security.AllowReconfiguration:
|
||||
MapAllowReconfiguration(settings, value);
|
||||
break;
|
||||
case Keys.Security.AllowStickyKeys:
|
||||
MapAllowStickyKeys(settings, value);
|
||||
break;
|
||||
case Keys.Security.AllowTermination:
|
||||
MapAllowTermination(settings, value);
|
||||
break;
|
||||
|
@ -47,6 +50,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.Security.VerifyCursorConfiguration:
|
||||
MapVerifyCursorConfiguration(settings, value);
|
||||
break;
|
||||
case Keys.Security.VerifySessionIntegrity:
|
||||
MapVerifySessionIntegrity(settings, value);
|
||||
break;
|
||||
case Keys.Security.VersionRestrictions:
|
||||
MapVersionRestrictions(settings, value);
|
||||
break;
|
||||
|
@ -75,6 +81,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
}
|
||||
}
|
||||
|
||||
private void MapAllowStickyKeys(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
{
|
||||
settings.Security.AllowStickyKeys = allow;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapAllowTermination(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool allow)
|
||||
|
@ -94,12 +108,12 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
|
||||
if (settings.Security.AllowApplicationLogAccess)
|
||||
{
|
||||
settings.ActionCenter.ShowApplicationLog = true;
|
||||
settings.UserInterface.ActionCenter.ShowApplicationLog = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.ActionCenter.ShowApplicationLog = false;
|
||||
settings.Taskbar.ShowApplicationLog = false;
|
||||
settings.UserInterface.ActionCenter.ShowApplicationLog = false;
|
||||
settings.UserInterface.Taskbar.ShowApplicationLog = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -175,6 +189,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
}
|
||||
}
|
||||
|
||||
private void MapVerifySessionIntegrity(AppSettings settings, object value)
|
||||
{
|
||||
if (value is bool verify)
|
||||
{
|
||||
settings.Security.VerifySessionIntegrity = verify;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapVersionRestrictions(AppSettings settings, object value)
|
||||
{
|
||||
if (value is IList<object> restrictions)
|
||||
|
|
|
@ -20,6 +20,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
case Keys.UserInterface.ActionCenter.EnableActionCenter:
|
||||
MapEnableActionCenter(settings, value);
|
||||
break;
|
||||
case Keys.UserInterface.LockScreen.BackgroundColor:
|
||||
MapLockScreenBackgroundColor(settings, value);
|
||||
break;
|
||||
case Keys.UserInterface.SystemControls.Audio.Show:
|
||||
MapShowAudio(settings, value);
|
||||
break;
|
||||
|
@ -54,7 +57,15 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool enable)
|
||||
{
|
||||
settings.ActionCenter.EnableActionCenter = enable;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = enable;
|
||||
}
|
||||
}
|
||||
|
||||
private void MapLockScreenBackgroundColor(AppSettings settings, object value)
|
||||
{
|
||||
if (value is string color)
|
||||
{
|
||||
settings.UserInterface.LockScreen.BackgroundColor = color;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -62,8 +73,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.ActionCenter.ShowAudio = show;
|
||||
settings.Taskbar.ShowAudio = show;
|
||||
settings.UserInterface.ActionCenter.ShowAudio = show;
|
||||
settings.UserInterface.Taskbar.ShowAudio = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -71,8 +82,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.ActionCenter.ShowClock = show;
|
||||
settings.Taskbar.ShowClock = show;
|
||||
settings.UserInterface.ActionCenter.ShowClock = show;
|
||||
settings.UserInterface.Taskbar.ShowClock = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -80,8 +91,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.ActionCenter.ShowKeyboardLayout = show;
|
||||
settings.Taskbar.ShowKeyboardLayout = show;
|
||||
settings.UserInterface.ActionCenter.ShowKeyboardLayout = show;
|
||||
settings.UserInterface.Taskbar.ShowKeyboardLayout = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,8 +100,8 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.ActionCenter.ShowNetwork = show;
|
||||
settings.Taskbar.ShowNetwork = show;
|
||||
settings.UserInterface.ActionCenter.ShowNetwork = show;
|
||||
settings.UserInterface.Taskbar.ShowNetwork = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,7 +125,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool enable)
|
||||
{
|
||||
settings.Taskbar.EnableTaskbar = enable;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = enable;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -122,7 +133,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool show)
|
||||
{
|
||||
settings.Taskbar.ShowApplicationLog = show;
|
||||
settings.UserInterface.Taskbar.ShowApplicationLog = show;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +141,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is bool mobile)
|
||||
{
|
||||
settings.UserInterfaceMode = mobile ? UserInterfaceMode.Mobile : UserInterfaceMode.Desktop;
|
||||
settings.UserInterface.Mode = mobile ? UserInterfaceMode.Mobile : UserInterfaceMode.Desktop;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,6 @@ using System.IO;
|
|||
using System.Security.Cryptography;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Applications;
|
||||
using SafeExamBrowser.Settings.Proctoring;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||
|
@ -21,8 +20,13 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
{
|
||||
internal void Process(IDictionary<string, object> rawData, AppSettings settings)
|
||||
{
|
||||
AllowBrowserToolbarForReloading(settings);
|
||||
ProcessDefault(settings);
|
||||
CalculateConfigurationKey(rawData, settings);
|
||||
}
|
||||
|
||||
internal void ProcessDefault(AppSettings settings)
|
||||
{
|
||||
AllowBrowserToolbarForReloading(settings);
|
||||
InitializeBrowserHomeFunctionality(settings);
|
||||
InitializeClipboardSettings(settings);
|
||||
InitializeProctoringSettings(settings);
|
||||
|
@ -77,15 +81,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
private void InitializeProctoringSettings(AppSettings settings)
|
||||
{
|
||||
settings.Proctoring.Enabled = settings.Proctoring.ScreenProctoring.Enabled;
|
||||
|
||||
// The video proctoring implementations are disabled for version 3.7.0 and will be completely removed for version 3.8.0.
|
||||
settings.Proctoring.JitsiMeet.Enabled = false;
|
||||
settings.Proctoring.Zoom.Enabled = false;
|
||||
|
||||
if (settings.Proctoring.JitsiMeet.Enabled && !settings.Proctoring.JitsiMeet.ReceiveVideo)
|
||||
{
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
|
||||
}
|
||||
}
|
||||
|
||||
private void RemoveLegacyBrowsers(AppSettings settings)
|
||||
|
|
|
@ -100,13 +100,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
{
|
||||
var settings = new AppSettings();
|
||||
|
||||
settings.ActionCenter.EnableActionCenter = true;
|
||||
settings.ActionCenter.ShowApplicationInfo = true;
|
||||
settings.ActionCenter.ShowApplicationLog = false;
|
||||
settings.ActionCenter.ShowClock = true;
|
||||
settings.ActionCenter.ShowKeyboardLayout = true;
|
||||
settings.ActionCenter.ShowNetwork = false;
|
||||
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "AA_v3.exe", OriginalName = "AA_v3.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "AeroAdmin.exe", OriginalName = "AeroAdmin.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "beamyourscreen-host.exe", OriginalName = "beamyourscreen-host.exe" });
|
||||
|
@ -122,7 +115,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "Discord.exe", OriginalName = "Discord.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "Element.exe", OriginalName = "Element.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "g2mcomm.exe", OriginalName = "g2mcomm.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "g2mcomm.exe", OriginalName = "g2mcomm.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "g2mlauncher.exe", OriginalName = "g2mlauncher.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "g2mstart.exe", OriginalName = "g2mstart.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "GotoMeetingWinStore.exe", OriginalName = "GotoMeetingWinStore.exe" });
|
||||
|
@ -140,6 +132,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "remoting_host.exe", OriginalName = "remoting_host.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "RPCService.exe", OriginalName = "RPCService.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "RPCSuite.exe", OriginalName = "RPCSuite.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "sethc.exe", OriginalName = "sethc.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "Skype.exe", OriginalName = "Skype.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "SkypeApp.exe", OriginalName = "SkypeApp.exe" });
|
||||
settings.Applications.Blacklist.Add(new BlacklistApplication { ExecutableName = "SkypeHost.exe", OriginalName = "SkypeHost.exe" });
|
||||
|
@ -172,13 +165,13 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Browser.AdditionalWindow.UrlPolicy = UrlPolicy.Never;
|
||||
settings.Browser.AllowConfigurationDownloads = true;
|
||||
settings.Browser.AllowCustomDownAndUploadLocation = false;
|
||||
settings.Browser.AllowDownloads = false;
|
||||
settings.Browser.AllowDownloads = true;
|
||||
settings.Browser.AllowFind = true;
|
||||
settings.Browser.AllowPageZoom = true;
|
||||
settings.Browser.AllowPdfReader = true;
|
||||
settings.Browser.AllowPdfReaderToolbar = false;
|
||||
settings.Browser.AllowPrint = false;
|
||||
settings.Browser.AllowUploads = false;
|
||||
settings.Browser.AllowUploads = true;
|
||||
settings.Browser.DeleteCacheOnShutdown = true;
|
||||
settings.Browser.DeleteCookiesOnShutdown = true;
|
||||
settings.Browser.DeleteCookiesOnStartup = true;
|
||||
|
@ -203,6 +196,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Browser.ShowFileSystemElementPath = true;
|
||||
settings.Browser.StartUrl = "https://www.safeexambrowser.org/start";
|
||||
settings.Browser.UseCustomUserAgent = false;
|
||||
settings.Browser.UseIsolatedClipboard = true;
|
||||
settings.Browser.UseQueryParameter = false;
|
||||
settings.Browser.UseTemporaryDownAndUploadDirectory = false;
|
||||
|
||||
|
@ -216,7 +210,10 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Keyboard.AllowAltEsc = false;
|
||||
settings.Keyboard.AllowAltF4 = false;
|
||||
settings.Keyboard.AllowAltTab = true;
|
||||
settings.Keyboard.AllowCtrlC = true;
|
||||
settings.Keyboard.AllowCtrlEsc = false;
|
||||
settings.Keyboard.AllowCtrlV = true;
|
||||
settings.Keyboard.AllowCtrlX = true;
|
||||
settings.Keyboard.AllowEsc = true;
|
||||
settings.Keyboard.AllowF1 = true;
|
||||
settings.Keyboard.AllowF2 = true;
|
||||
|
@ -243,20 +240,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
|
||||
settings.Proctoring.Enabled = false;
|
||||
settings.Proctoring.ForceRaiseHandMessage = false;
|
||||
settings.Proctoring.JitsiMeet.AllowChat = false;
|
||||
settings.Proctoring.JitsiMeet.AllowClosedCaptions = false;
|
||||
settings.Proctoring.JitsiMeet.AllowRaiseHand = false;
|
||||
settings.Proctoring.JitsiMeet.AllowRecording = false;
|
||||
settings.Proctoring.JitsiMeet.AllowTileView = false;
|
||||
settings.Proctoring.JitsiMeet.AudioMuted = true;
|
||||
settings.Proctoring.JitsiMeet.AudioOnly = false;
|
||||
settings.Proctoring.JitsiMeet.Enabled = false;
|
||||
settings.Proctoring.JitsiMeet.ReceiveAudio = false;
|
||||
settings.Proctoring.JitsiMeet.ReceiveVideo = false;
|
||||
settings.Proctoring.JitsiMeet.SendAudio = true;
|
||||
settings.Proctoring.JitsiMeet.SendVideo = true;
|
||||
settings.Proctoring.JitsiMeet.ShowMeetingName = false;
|
||||
settings.Proctoring.JitsiMeet.VideoMuted = false;
|
||||
settings.Proctoring.ScreenProctoring.Enabled = false;
|
||||
settings.Proctoring.ScreenProctoring.ImageDownscaling = 1.0;
|
||||
settings.Proctoring.ScreenProctoring.ImageFormat = ImageFormat.Png;
|
||||
|
@ -268,25 +251,16 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
settings.Proctoring.ScreenProctoring.MinInterval = 1000;
|
||||
settings.Proctoring.ShowRaiseHandNotification = true;
|
||||
settings.Proctoring.ShowTaskbarNotification = true;
|
||||
settings.Proctoring.WindowVisibility = WindowVisibility.Hidden;
|
||||
settings.Proctoring.Zoom.AllowChat = false;
|
||||
settings.Proctoring.Zoom.AllowClosedCaptions = false;
|
||||
settings.Proctoring.Zoom.AllowRaiseHand = false;
|
||||
settings.Proctoring.Zoom.AudioMuted = true;
|
||||
settings.Proctoring.Zoom.Enabled = false;
|
||||
settings.Proctoring.Zoom.ReceiveAudio = false;
|
||||
settings.Proctoring.Zoom.ReceiveVideo = false;
|
||||
settings.Proctoring.Zoom.SendAudio = true;
|
||||
settings.Proctoring.Zoom.SendVideo = true;
|
||||
settings.Proctoring.Zoom.VideoMuted = false;
|
||||
|
||||
settings.Security.AllowApplicationLogAccess = false;
|
||||
settings.Security.AllowTermination = true;
|
||||
settings.Security.AllowReconfiguration = false;
|
||||
settings.Security.AllowStickyKeys = false;
|
||||
settings.Security.ClipboardPolicy = ClipboardPolicy.Isolated;
|
||||
settings.Security.DisableSessionChangeLockScreen = false;
|
||||
settings.Security.KioskMode = KioskMode.CreateNewDesktop;
|
||||
settings.Security.VerifyCursorConfiguration = true;
|
||||
settings.Security.VerifySessionIntegrity = true;
|
||||
settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny;
|
||||
|
||||
settings.Server.PingInterval = 1000;
|
||||
|
@ -316,14 +290,20 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
|
||||
settings.System.AlwaysOn = true;
|
||||
|
||||
settings.Taskbar.EnableTaskbar = true;
|
||||
settings.Taskbar.ShowApplicationInfo = false;
|
||||
settings.Taskbar.ShowApplicationLog = false;
|
||||
settings.Taskbar.ShowClock = true;
|
||||
settings.Taskbar.ShowKeyboardLayout = true;
|
||||
settings.Taskbar.ShowNetwork = false;
|
||||
|
||||
settings.UserInterfaceMode = UserInterfaceMode.Desktop;
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.ActionCenter.ShowApplicationInfo = true;
|
||||
settings.UserInterface.ActionCenter.ShowApplicationLog = false;
|
||||
settings.UserInterface.ActionCenter.ShowClock = true;
|
||||
settings.UserInterface.ActionCenter.ShowKeyboardLayout = true;
|
||||
settings.UserInterface.ActionCenter.ShowNetwork = false;
|
||||
settings.UserInterface.LockScreen.BackgroundColor = "#ff0000";
|
||||
settings.UserInterface.Mode = UserInterfaceMode.Desktop;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
settings.UserInterface.Taskbar.ShowApplicationInfo = false;
|
||||
settings.UserInterface.Taskbar.ShowApplicationLog = false;
|
||||
settings.UserInterface.Taskbar.ShowClock = true;
|
||||
settings.UserInterface.Taskbar.ShowKeyboardLayout = true;
|
||||
settings.UserInterface.Taskbar.ShowNetwork = false;
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
|
|
@ -234,29 +234,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string ForceRaiseHandMessage = "raiseHandButtonAlwaysPromptMessage";
|
||||
internal const string ShowRaiseHand = "raiseHandButtonShow";
|
||||
internal const string ShowTaskbarNotification = "showProctoringViewButton";
|
||||
internal const string WindowVisibility = "remoteProctoringViewShow";
|
||||
|
||||
internal static class JitsiMeet
|
||||
{
|
||||
internal const string AllowChat = "jitsiMeetFeatureFlagChat";
|
||||
internal const string AllowClosedCaptions = "jitsiMeetFeatureFlagCloseCaptions";
|
||||
internal const string AllowRaiseHand = "jitsiMeetFeatureFlagRaiseHand";
|
||||
internal const string AllowRecording = "jitsiMeetFeatureFlagRecording";
|
||||
internal const string AllowTileView = "jitsiMeetFeatureFlagTileView";
|
||||
internal const string AudioMuted = "jitsiMeetAudioMuted";
|
||||
internal const string AudioOnly = "jitsiMeetAudioOnly";
|
||||
internal const string Enabled = "jitsiMeetEnable";
|
||||
internal const string ReceiveAudio = "jitsiMeetReceiveAudio";
|
||||
internal const string ReceiveVideo = "jitsiMeetReceiveVideo";
|
||||
internal const string RoomName = "jitsiMeetRoom";
|
||||
internal const string SendAudio = "jitsiMeetSendAudio";
|
||||
internal const string SendVideo = "jitsiMeetSendVideo";
|
||||
internal const string ServerUrl = "jitsiMeetServerURL";
|
||||
internal const string ShowMeetingName = "jitsiMeetFeatureFlagDisplayMeetingName";
|
||||
internal const string Subject = "jitsiMeetSubject";
|
||||
internal const string Token = "jitsiMeetToken";
|
||||
internal const string VideoMuted = "jitsiMeetVideoMuted";
|
||||
}
|
||||
|
||||
internal static class ScreenProctoring
|
||||
{
|
||||
|
@ -278,24 +255,6 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string CaptureWindowTitle = "screenProctoringMetadataWindowTitleEnabled";
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Zoom
|
||||
{
|
||||
internal const string AllowChat = "zoomFeatureFlagChat";
|
||||
internal const string AllowClosedCaptions = "zoomFeatureFlagCloseCaptions";
|
||||
internal const string AllowRaiseHand = "zoomFeatureFlagRaiseHand";
|
||||
internal const string AudioMuted = "zoomAudioMuted";
|
||||
internal const string Enabled = "zoomEnable";
|
||||
internal const string MeetingNumber = "zoomRoom";
|
||||
internal const string ReceiveAudio = "zoomReceiveAudio";
|
||||
internal const string ReceiveVideo = "zoomReceiveVideo";
|
||||
internal const string SendAudio = "zoomSendAudio";
|
||||
internal const string SendVideo = "zoomSendVideo";
|
||||
internal const string Signature = "zoomToken";
|
||||
internal const string Subject = "zoomSubject";
|
||||
internal const string UserName = "zoomUserInfoDisplayName";
|
||||
internal const string VideoMuted = "zoomVideoMuted";
|
||||
}
|
||||
}
|
||||
|
||||
internal static class Security
|
||||
|
@ -303,6 +262,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string AdminPasswordHash = "hashedAdminPassword";
|
||||
internal const string AllowApplicationLog = "allowApplicationLog";
|
||||
internal const string AllowReconfiguration = "examSessionReconfigureAllow";
|
||||
internal const string AllowStickyKeys = "allowStickyKeys";
|
||||
internal const string AllowTermination = "allowQuit";
|
||||
internal const string AllowVirtualMachine = "allowVirtualMachine";
|
||||
internal const string ClipboardPolicy = "clipboardPolicy";
|
||||
|
@ -312,6 +272,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string QuitPasswordHash = "hashedQuitPassword";
|
||||
internal const string ReconfigurationUrl = "examSessionReconfigureConfigURL";
|
||||
internal const string VerifyCursorConfiguration = "enableCursorVerification";
|
||||
internal const string VerifySessionIntegrity = "enableSessionVerification";
|
||||
internal const string VersionRestrictions = "sebAllowedVersions";
|
||||
}
|
||||
|
||||
|
@ -366,6 +327,11 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
|||
internal const string EnableActionCenter = "showSideMenu";
|
||||
}
|
||||
|
||||
internal static class LockScreen
|
||||
{
|
||||
internal const string BackgroundColor = "lockScreenBackgroundColor";
|
||||
}
|
||||
|
||||
internal static class SystemControls
|
||||
{
|
||||
internal static class Audio
|
||||
|
|
|
@ -122,6 +122,8 @@ namespace SafeExamBrowser.Configuration
|
|||
var status = default(LoadStatus);
|
||||
|
||||
settings = LoadDefaultSettings();
|
||||
dataProcessor.ProcessDefault(settings);
|
||||
|
||||
logger.Info($"Initialized default settings, now attempting to load '{resource}'...");
|
||||
|
||||
try
|
||||
|
@ -136,7 +138,7 @@ namespace SafeExamBrowser.Configuration
|
|||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
dataMapper.MapRawDataToSettings(data, settings);
|
||||
dataMapper.Map(data, settings);
|
||||
dataProcessor.Process(data, settings);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
using System;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||
|
@ -20,7 +21,7 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
{
|
||||
private readonly object @lock = new object();
|
||||
|
||||
private readonly SHA256Managed algorithm;
|
||||
private readonly ThreadLocal<SHA256Managed> algorithm;
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly IIntegrityModule integrityModule;
|
||||
private readonly ILogger logger;
|
||||
|
@ -29,7 +30,7 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
|
||||
public KeyGenerator(AppConfig appConfig, IIntegrityModule integrityModule, ILogger logger)
|
||||
{
|
||||
this.algorithm = new SHA256Managed();
|
||||
this.algorithm = new ThreadLocal<SHA256Managed>(() => new SHA256Managed());
|
||||
this.appConfig = appConfig;
|
||||
this.integrityModule = integrityModule;
|
||||
this.logger = logger;
|
||||
|
@ -52,7 +53,7 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
public string CalculateBrowserExamKeyHash(string configurationKey, byte[] salt, string url)
|
||||
{
|
||||
var urlWithoutFragment = url.Split('#')[0];
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey(configurationKey, salt))));
|
||||
var hash = algorithm.Value.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey(configurationKey, salt))));
|
||||
var key = ToString(hash);
|
||||
|
||||
return key;
|
||||
|
@ -61,7 +62,7 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
public string CalculateConfigurationKeyHash(string configurationKey, string url)
|
||||
{
|
||||
var urlWithoutFragment = url.Split('#')[0];
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + configurationKey));
|
||||
var hash = algorithm.Value.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + configurationKey));
|
||||
var key = ToString(hash);
|
||||
|
||||
return key;
|
||||
|
@ -111,6 +112,8 @@ namespace SafeExamBrowser.Configuration.Cryptography
|
|||
|
||||
browserExamKey = key;
|
||||
}
|
||||
|
||||
logger.Debug("Successfully calculated browser exam key using simplified calculation.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -149,7 +149,7 @@ namespace SafeExamBrowser.Configuration.DataResources
|
|||
{
|
||||
data = default;
|
||||
|
||||
logger.Debug($"The {(IsUnauthorized(response) ? "resource needs authentication" : " response data is HTML")}.");
|
||||
logger.Debug($"The {(IsUnauthorized(response) ? "resource needs authentication" : "response data is HTML")}.");
|
||||
|
||||
return LoadStatus.LoadWithBrowser;
|
||||
}
|
||||
|
|
|
@ -47,6 +47,11 @@ namespace SafeExamBrowser.I18n.Contracts
|
|||
BrowserWindow_ZoomMenuMinus,
|
||||
BrowserWindow_ZoomMenuPlus,
|
||||
Build,
|
||||
CredentialsDialog_PasswordLabel,
|
||||
CredentialsDialog_UsernameLabel,
|
||||
CredentialsDialog_UsernameOptionalLabel,
|
||||
CredentialsDialog_WirelessNetworkMessage,
|
||||
CredentialsDialog_WirelessNetworkTitle,
|
||||
ExamSelectionDialog_Cancel,
|
||||
ExamSelectionDialog_Message,
|
||||
ExamSelectionDialog_Select,
|
||||
|
@ -78,6 +83,9 @@ namespace SafeExamBrowser.I18n.Contracts
|
|||
LockScreen_EaseOfAccessMessage,
|
||||
LockScreen_EaseOfAccessTerminateOption,
|
||||
LockScreen_SessionIntegrityMessage,
|
||||
LockScreen_StickyKeysMessage,
|
||||
LockScreen_StickyKeysContinueOption,
|
||||
LockScreen_StickyKeysTerminateOption,
|
||||
LockScreen_Title,
|
||||
LockScreen_UnlockButton,
|
||||
LockScreen_UserSessionContinueOption,
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Passwort:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Benutzername:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Benutzername (falls erforderlich):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Bitte geben Sie die erforderlichen Anmeldeinformationen ein, um eine Verbindung mit dem drahtlosen Netzwerk "%%_NAME_%%" herzustellen.
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Netzwerk-Authentifizierung erforderlich
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Abbrechen
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
Die letzte Sitzung mit der momentan aktiven Konfiguration oder Start-URL wurde nicht ordnungsgemäss beendet! Geben Sie bitte das korrekte Passwort ein, um SEB zu entsperren.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Zustand der Sticky-Keys temporär erlauben. Dies gilt nur für die momentan laufende Sitzung!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Ein verbotener Zustand der Sticky-Keys wurde detektiert. Bitte wählen Sie eine der verfügbaren Optionen aus und geben Sie das korrekte Passwort ein, um SEB zu entsperren.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Safe Exam Browser beenden. WARNUNG: Sie werden keine Möglichkeit haben, Daten zu speichern oder weitere Aktionen auszuführen, SEB wird sofort beendet!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB GESPERRT
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Password:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Username:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Username (if required):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Please enter the required credentials to establish a connection to the wireless network "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Network Authentication Required
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Cancel
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
The last session with the currently active configuration or start URL was not terminated properly! Please enter the correct password to unlock SEB.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Temporarily allow the state of the sticky keys. This applies only to the currently running session!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
A prohibited state of the sticky keys has been detected. In order to unlock SEB, please select one of the available options and enter the correct unlock password.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB LOCKED
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Contraseña:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Nombre de usuario:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Nombre de usuario (si se requiere):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Por favor, introduzca las credenciales necesarias para establecer una conexión con la red inalámbrica "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Autenticación de red necesaria
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Cancel
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
La última sesión con la configuración activa o la URL de inicio no se ha cerrado correctamente. Por favor, introduzca la contraseña correcta para desbloquear SEB.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Permitir temporalmente el estado de las teclas adhesivas. Esto sólo se aplica a la sesión que se está ejecutando en ese momento.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Se ha detectado un estado prohibido de las teclas adhesivas. Para desbloquear SEB, seleccione una de las opciones disponibles e introduzca la contraseña de desbloqueo correcta.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Finalizar Safe Exam Browser. ADVERTENCIA: No habrá posibilidad de guardar datos o realizar ninguna otra acción, ¡el cierre se iniciará inmediatamente!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB BLOQUEADO
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Koosta
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Parool:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Kasutajanimi:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Kasutajanimi (kui see on nõutav):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Palun sisestage vajalikud volitused, et luua ühendus traadita võrguga "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Võrguautentimine nõutav
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Tühista
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
Viimast seanssi hetkel aktiivse seadistuse või algus-URL-iga ei suletud korrektselt! Palun sisestage SEB'i avamiseks õige parool.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Lubab ajutiselt kleepuvate võtmete oleku. See kehtib ainult hetkel käimasoleva seansi kohta!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Avastati kleepuvate võtmete keelatud olek. SEB avamiseks valige üks olemasolevatest võimalustest ja sisestage õige avamisparool.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Sulge Safe Exam Browser. HOIATUS: andmete salvestamise ega edasiste toimingute tegemise võimalust ei ole, sulgemine käivitatakse kohe!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB ON LUKUSTATUD
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Mot de passe:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Nom d'utilisateur:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Nom d'utilisateur (si requis):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Veuillez saisir les informations d'identification requises pour établir une connexion au réseau sans fil "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Authentification réseau requise
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Annuler
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
La dernière session avec la configuration ou l'URL de démarrage actuellement active n'a pas été terminée correctement! Afin de déverrouiller SEB, veuillez entrer le mot de passe de déverrouillage correct.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Autoriser temporairement l'état des clés autocollantes. Cela ne s'applique qu'à la session en cours!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Un état d'interdiction des clés autocollantes a été détecté. Pour déverrouiller SEB, veuillez sélectionner l'une des options disponibles et saisir le mot de passe de déverrouillage correct.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Terminate Safe Exam Browser. WARNING: There will be no possibility to save data or perform any further actions, the shutdown will be initiated immediately!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB VEROUILLE
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Bangun
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Kata sandi:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Nama pengguna:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Nama pengguna (jika diperlukan):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Masukkan kredensial yang diperlukan untuk membuat sambungan ke jaringan nirkabel "%%_NAMA_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Diperlukan Otentikasi Jaringan
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Batal
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
Sesi terakhir dengan konfigurasi yang sedang aktif atau URL awal tidak diakhiri dengan benar! Silakan masukkan kata sandi yang benar untuk membuka kunci SEB.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Mengizinkan status tombol tempel untuk sementara waktu. Ini hanya berlaku untuk sesi yang sedang berjalan!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Kondisi kunci tempel yang dilarang telah terdeteksi. Untuk membuka kunci SEB, pilih salah satu opsi yang tersedia dan masukkan kata sandi buka kunci yang benar.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Menghentikan Safe Exam Browser. PERINGATAN: Tidak akan ada kemungkinan untuk menyimpan data atau melakukan tindakan lebih lanjut, penghentian akan segera dilakukan!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB TERKUNCI
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Password:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Nome utente:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Nome utente (se richiesto):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Inserire le credenziali richieste per stabilire una connessione alla rete wireless "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
È richiesta l'autenticazione di rete
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Annulla
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
L'ultima sessione con la configurazione o l'URL di avvio attualmente attiva non è stata terminata correttamente! Per sbloccare SEB, inserisci la password di sblocco corretta.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Consente temporaneamente lo stato dei tasti adesivi. Questo vale solo per la sessione in corso!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
È stato rilevato uno stato non consentito dei tasti adesivi. Per sbloccare SEB, selezionare una delle opzioni disponibili e inserire la password di sblocco corretta.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Terminare Safe Exam Browser. ATTENZIONE: Non sarà possibile salvare i dati o eseguire ulteriori azioni, la chiusura verrà avviata immediatamente!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB BLOCCATO
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Build
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Wachtwoord:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Gebruikersnaam:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Gebruikersnaam (indien vereist):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Voer de vereiste gegevens in om verbinding te maken met het draadloze netwerk “%%_NAME_%%”.
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Netwerkverificatie vereist
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Annuleren
|
||||
</Entry>
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
De laatste sessie met de huidige actieve configuratie of start URL is niet goed afgesloten! Voer het juiste wachtwoord in om SEB te ontgrendelen.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Tijdelijk de toestand van de sticky keys toestaan. Dit geldt alleen voor de huidige sessie!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Er is een verboden toestand van de sticky keys gedetecteerd. Om SEB te ontgrendelen, selecteert u een van de beschikbare opties en voert u het juiste ontgrendelwachtwoord in.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Beëindig de Safe Exam Browser. WAARSCHUWING: er is geen mogelijkheid om gegevens op te slaan of verdere acties uit te voeren, de browser wordt onmiddellijk afgesloten!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB VERGRENDELD
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
Строить
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
Пароль:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
Имя пользователя:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
Имя пользователя (если требуется):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
Введите необходимые учетные данные для установления соединения с беспроводной сетью "%%_NAME_%%".
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
Требуется сетевая аутентификация
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
Отмена
|
||||
</Entry>
|
||||
|
@ -191,6 +206,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
Последний сеанс с текущей активной конфигурацией или URL-адресом запуска не был завершен должным образом! Пожалуйста, введите правильный пароль, чтобы разблокировать SEB.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
Временно разрешить состояние липких ключей. Это относится только к текущему сеансу!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
Обнаружено запрещенное состояние липких клавиш. Чтобы разблокировать SEB, пожалуйста, выберите один из доступных вариантов и введите правильный пароль разблокировки.
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
Завершить работу Safe Exam Browser. ВНИМАНИЕ: Сохранить данные или выполнить какие-либо дальнейшие действия будет невозможно, завершение работы будет инициировано немедленно!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
SEB ЗАБЛОКИРОВАН
|
||||
</Entry>
|
||||
|
|
|
@ -99,6 +99,21 @@
|
|||
<Entry key="Build">
|
||||
生成
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_PasswordLabel">
|
||||
密码:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameLabel">
|
||||
用户名:
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_UsernameOptionalLabel">
|
||||
用户名(如需要):
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkMessage">
|
||||
请输入与无线网络 "%%_NAME_%%" 建立连接所需的凭证。
|
||||
</Entry>
|
||||
<Entry key="CredentialsDialog_WirelessNetworkTitle">
|
||||
需要网络验证
|
||||
</Entry>
|
||||
<Entry key="ExamSelectionDialog_Cancel">
|
||||
取消
|
||||
</Entry>
|
||||
|
@ -178,7 +193,7 @@
|
|||
检测到禁止的显示配置。 要解锁 SEB,请选择可用选项之一并输入正确的解锁密码。
|
||||
</Entry>
|
||||
<Entry key="LockScreen_DisplayConfigurationTerminateOption">
|
||||
终止安全考试浏览器。 警告:将无法保存数据或执行任何进一步操作,将立即启动关机!
|
||||
终止安全考试浏览器。警告:将无法保存数据或执行任何进一步操作,将立即启动关机!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_EaseOfAccessContinueOption">
|
||||
暂时允许轻松访问的配置。这只适用于当前运行的会话!
|
||||
|
@ -192,6 +207,15 @@
|
|||
<Entry key="LockScreen_SessionIntegrityMessage">
|
||||
当前激活的配置或启动URL的最后一个会话没有被正确终止! 请输入正确的密码以解锁SEB。
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysContinueOption">
|
||||
暂时允许粘性按键的状态。这仅适用于当前运行的会话!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysMessage">
|
||||
已检测到粘滞键处于禁用状态。要解锁 SEB,请选择一个可用选项并输入正确的解锁密码。
|
||||
</Entry>
|
||||
<Entry key="LockScreen_StickyKeysTerminateOption">
|
||||
终止安全考试浏览器。警告:将没有可能保存数据或执行任何进一步的行动,关闭将立即启动!
|
||||
</Entry>
|
||||
<Entry key="LockScreen_Title">
|
||||
防作弊考试专用浏览器已锁定
|
||||
</Entry>
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.SystemComponents.Contracts
|
||||
namespace SafeExamBrowser.Monitoring.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality related to remote session detection.
|
|
@ -6,7 +6,7 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.SystemComponents.Contracts
|
||||
namespace SafeExamBrowser.Monitoring.Contracts
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides functionality related to virtual machine detection.
|
|
@ -65,13 +65,17 @@
|
|||
<Compile Include="Display\IDisplayMonitor.cs" />
|
||||
<Compile Include="Display\ValidationResult.cs" />
|
||||
<Compile Include="IClipboard.cs" />
|
||||
<Compile Include="IRemoteSessionDetector.cs" />
|
||||
<Compile Include="IVirtualMachineDetector.cs" />
|
||||
<Compile Include="Keyboard\IKeyboardInterceptor.cs" />
|
||||
<Compile Include="Mouse\IMouseInterceptor.cs" />
|
||||
<Compile Include="Applications\InitializationResult.cs" />
|
||||
<Compile Include="Applications\IApplicationMonitor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="System\Events\SentinelEventArgs.cs" />
|
||||
<Compile Include="System\Events\SentinelEventHandler.cs" />
|
||||
<Compile Include="System\Events\SessionChangedEventHandler.cs" />
|
||||
<Compile Include="System\ISystemMonitor.cs" />
|
||||
<Compile Include="System\ISystemSentinel.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Settings\SafeExamBrowser.Settings.csproj">
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Contracts.System.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// The event arguments used for the <see cref="SentinelEventHandler"/> fired by the <see cref="ISystemSentinel"/>.
|
||||
/// </summary>
|
||||
public class SentinelEventArgs
|
||||
{
|
||||
/// <summary>
|
||||
/// Indicates whether a configuration or state change shall be allowed.
|
||||
/// </summary>
|
||||
public bool Allow { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Contracts.System.Events
|
||||
{
|
||||
/// <summary>
|
||||
/// The default handler for events of the <see cref="ISystemSentinel"/>. Normally indicates that a configuration or state has changed.
|
||||
/// </summary>
|
||||
public delegate void SentinelEventHandler(SentinelEventArgs args);
|
||||
}
|
|
@ -1,33 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Monitoring.Contracts.System.Events;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Contracts.System
|
||||
{
|
||||
/// <summary>
|
||||
/// Monitors the operating system, e.g. for user account related events.
|
||||
/// </summary>
|
||||
public interface ISystemMonitor
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when the active user session has changed.
|
||||
/// </summary>
|
||||
event SessionChangedEventHandler SessionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Starts the monitoring.
|
||||
/// </summary>
|
||||
void Start();
|
||||
|
||||
/// <summary>
|
||||
/// Stops the monitoring.
|
||||
/// </summary>
|
||||
void Stop();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Monitoring.Contracts.System.Events;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.Contracts.System
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides the possibility to suppress and surveil various system components and functionalities.
|
||||
/// </summary>
|
||||
public interface ISystemSentinel
|
||||
{
|
||||
/// <summary>
|
||||
/// Event fired when the cursor configuration has changed.
|
||||
/// </summary>
|
||||
event SentinelEventHandler CursorChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the ease of access configuration has changed.
|
||||
/// </summary>
|
||||
event SentinelEventHandler EaseOfAccessChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the active user session has changed.
|
||||
/// </summary>
|
||||
event SessionChangedEventHandler SessionChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when the sticky keys state has changed.
|
||||
/// </summary>
|
||||
event SentinelEventHandler StickyKeysChanged;
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to disable the sticky keys. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool DisableStickyKeys();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to enable the sticky keys. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool EnableStickyKeys();
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to revert the sticky keys to their initial state. Returns <c>true</c> if successful, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool RevertStickyKeys();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the cursor configuration.
|
||||
/// </summary>
|
||||
void StartMonitoringCursors();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the ease of access configuration.
|
||||
/// </summary>
|
||||
void StartMonitoringEaseOfAccess();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the sticky keys state.
|
||||
/// </summary>
|
||||
void StartMonitoringStickyKeys();
|
||||
|
||||
/// <summary>
|
||||
/// Starts monitoring the system events.
|
||||
/// </summary>
|
||||
void StartMonitoringSystemEvents();
|
||||
|
||||
/// <summary>
|
||||
/// Stops all monitoring operations.
|
||||
/// </summary>
|
||||
void StopMonitoring();
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the cursor configuration. Returns <c>true</c> if permitted, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool VerifyCursors();
|
||||
|
||||
/// <summary>
|
||||
/// Verifies the ease of access configuration. Returns <c>true</c> if permitted, otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
bool VerifyEaseOfAccess();
|
||||
}
|
||||
}
|
|
@ -98,7 +98,13 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
|
||||
if (activeWindow != default && TryGetProcessFor(activeWindow, out var process))
|
||||
{
|
||||
application = new ActiveApplication(process, new Window { Handle = activeWindow.Handle, Title = activeWindow.Title });
|
||||
var window = new Window
|
||||
{
|
||||
Handle = activeWindow.Handle,
|
||||
Title = nativeMethods.GetWindowTitle(activeWindow.Handle)
|
||||
};
|
||||
|
||||
application = new ActiveApplication(process, window);
|
||||
}
|
||||
|
||||
return application != default;
|
||||
|
@ -242,7 +248,6 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
{
|
||||
var isClient = true;
|
||||
var isRuntime = true;
|
||||
var isWebView = true;
|
||||
|
||||
isClient &= process.Name == "SafeExamBrowser.Client.exe";
|
||||
isClient &= process.OriginalName == "SafeExamBrowser.Client.exe";
|
||||
|
@ -250,16 +255,12 @@ namespace SafeExamBrowser.Monitoring.Applications
|
|||
isRuntime &= process.Name == "SafeExamBrowser.exe";
|
||||
isRuntime &= process.OriginalName == "SafeExamBrowser.exe";
|
||||
|
||||
isWebView &= process.Name == "msedgewebview2.exe";
|
||||
isWebView &= process.OriginalName == "msedgewebview2.exe";
|
||||
|
||||
#if !DEBUG
|
||||
isClient &= process.Signature == "2bc82fe8e56a39f96bc6c4b91d6703a0379b76a2";
|
||||
isRuntime &= process.Signature == "2bc82fe8e56a39f96bc6c4b91d6703a0379b76a2";
|
||||
isWebView &= process.Signature == "a4baabd12432ab9c7c297385260e95c3dae83bf2";
|
||||
#endif
|
||||
|
||||
return isClient || isRuntime || isWebView;
|
||||
return isClient || isRuntime;
|
||||
}
|
||||
|
||||
private void Close(Window window)
|
||||
|
|
|
@ -8,9 +8,9 @@
|
|||
|
||||
using System.Windows.Forms;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.SystemComponents
|
||||
namespace SafeExamBrowser.Monitoring
|
||||
{
|
||||
public class RemoteSessionDetector : IRemoteSessionDetector
|
||||
{
|
|
@ -67,7 +67,13 @@
|
|||
<Compile Include="Mouse\MouseInterceptor.cs" />
|
||||
<Compile Include="Applications\ApplicationMonitor.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="System\SystemMonitor.cs" />
|
||||
<Compile Include="RemoteSessionDetector.cs" />
|
||||
<Compile Include="System\Components\Cursors.cs" />
|
||||
<Compile Include="System\Components\EaseOfAccess.cs" />
|
||||
<Compile Include="System\Components\StickyKeys.cs" />
|
||||
<Compile Include="System\Components\SystemEvents.cs" />
|
||||
<Compile Include="System\SystemSentinel.cs" />
|
||||
<Compile Include="VirtualMachineDetector.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\SafeExamBrowser.Logging.Contracts\SafeExamBrowser.Logging.Contracts.csproj">
|
||||
|
|
152
SafeExamBrowser.Monitoring/System/Components/Cursors.cs
Normal file
152
SafeExamBrowser.Monitoring/System/Components/Cursors.cs
Normal file
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.System.Components
|
||||
{
|
||||
internal class Cursors
|
||||
{
|
||||
private static readonly string SYSTEM_PATH = $@"{Environment.ExpandEnvironmentVariables("%SystemRoot%")}\Cursors\";
|
||||
private static readonly string USER_PATH = $@"{Environment.ExpandEnvironmentVariables("%LocalAppData%")}\Microsoft\Windows\Cursors\";
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly IRegistry registry;
|
||||
|
||||
internal event SentinelEventHandler CursorChanged;
|
||||
|
||||
internal Cursors(ILogger logger, IRegistry registry)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
internal void StartMonitoring()
|
||||
{
|
||||
registry.ValueChanged += Registry_ValueChanged;
|
||||
|
||||
if (registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var names))
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
registry.StartMonitoring(RegistryValue.UserHive.Cursors_Key, name);
|
||||
}
|
||||
|
||||
logger.Info("Started monitoring cursors.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to start monitoring cursor registry values!");
|
||||
}
|
||||
}
|
||||
|
||||
internal void StopMonitoring()
|
||||
{
|
||||
registry.ValueChanged -= Registry_ValueChanged;
|
||||
|
||||
if (registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var names))
|
||||
{
|
||||
foreach (var name in names)
|
||||
{
|
||||
registry.StopMonitoring(RegistryValue.UserHive.Cursors_Key, name);
|
||||
}
|
||||
|
||||
logger.Info("Stopped monitoring cursors.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to stop monitoring cursor registry values!");
|
||||
}
|
||||
}
|
||||
|
||||
internal bool Verify()
|
||||
{
|
||||
logger.Info($"Starting cursor verification...");
|
||||
|
||||
var success = registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var cursors);
|
||||
|
||||
if (success)
|
||||
{
|
||||
foreach (var cursor in cursors.Where(c => !string.IsNullOrWhiteSpace(c)))
|
||||
{
|
||||
success &= VerifyCursor(cursor);
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Cursor configuration successfully verified.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Cursor configuration is compromised!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to verify cursor configuration!");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void Registry_ValueChanged(string key, string name, object oldValue, object newValue)
|
||||
{
|
||||
if (key == RegistryValue.UserHive.Cursors_Key)
|
||||
{
|
||||
HandleCursorChange(key, name, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleCursorChange(string key, string name, object oldValue, object newValue)
|
||||
{
|
||||
var args = new SentinelEventArgs();
|
||||
|
||||
logger.Warn($@"The cursor registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'!");
|
||||
|
||||
Task.Run(() => CursorChanged?.Invoke(args)).ContinueWith((_) =>
|
||||
{
|
||||
if (args.Allow)
|
||||
{
|
||||
registry.StopMonitoring(key, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool VerifyCursor(string cursor)
|
||||
{
|
||||
var success = true;
|
||||
|
||||
success &= registry.TryRead(RegistryValue.UserHive.Cursors_Key, cursor, out var value);
|
||||
success &= !(value is string) || (value is string path && (string.IsNullOrWhiteSpace(path) || IsValidCursorPath(path)));
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (value != default)
|
||||
{
|
||||
logger.Warn($"Configuration of cursor '{cursor}' is compromised: '{value}'!");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Failed to verify configuration of cursor '{cursor}'!");
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool IsValidCursorPath(string path)
|
||||
{
|
||||
return path.StartsWith(USER_PATH, StringComparison.OrdinalIgnoreCase) || path.StartsWith(SYSTEM_PATH, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
}
|
||||
}
|
95
SafeExamBrowser.Monitoring/System/Components/EaseOfAccess.cs
Normal file
95
SafeExamBrowser.Monitoring/System/Components/EaseOfAccess.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Threading.Tasks;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.System.Components
|
||||
{
|
||||
internal class EaseOfAccess
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IRegistry registry;
|
||||
|
||||
internal event SentinelEventHandler EaseOfAccessChanged;
|
||||
|
||||
internal EaseOfAccess(ILogger logger, IRegistry registry)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.registry = registry;
|
||||
}
|
||||
|
||||
internal void StartMonitoring()
|
||||
{
|
||||
registry.ValueChanged += Registry_ValueChanged;
|
||||
registry.StartMonitoring(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name);
|
||||
|
||||
logger.Info("Started monitoring ease of access.");
|
||||
}
|
||||
|
||||
internal void StopMonitoring()
|
||||
{
|
||||
registry.ValueChanged -= Registry_ValueChanged;
|
||||
registry.StopMonitoring(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name);
|
||||
|
||||
logger.Info("Stopped monitoring ease of access.");
|
||||
}
|
||||
|
||||
internal bool Verify()
|
||||
{
|
||||
logger.Info($"Starting ease of access verification...");
|
||||
|
||||
var success = registry.TryRead(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name, out var value);
|
||||
|
||||
if (success)
|
||||
{
|
||||
if (value is string s && string.IsNullOrWhiteSpace(s))
|
||||
{
|
||||
logger.Info("Ease of access configuration successfully verified.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Ease of access configuration is compromised: '{value}'!");
|
||||
success = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
logger.Info("Ease of access configuration successfully verified (value does not exist).");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void Registry_ValueChanged(string key, string name, object oldValue, object newValue)
|
||||
{
|
||||
if (key == RegistryValue.MachineHive.EaseOfAccess_Key)
|
||||
{
|
||||
HandleEaseOfAccessChange(key, name, oldValue, newValue);
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleEaseOfAccessChange(string key, string name, object oldValue, object newValue)
|
||||
{
|
||||
var args = new SentinelEventArgs();
|
||||
|
||||
logger.Warn($@"The ease of access registry value '{key}\{name}' has changed from '{oldValue}' to '{newValue}'!");
|
||||
|
||||
Task.Run(() => EaseOfAccessChanged?.Invoke(args)).ContinueWith((_) =>
|
||||
{
|
||||
if (args.Allow)
|
||||
{
|
||||
registry.StopMonitoring(key, name);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
175
SafeExamBrowser.Monitoring/System/Components/StickyKeys.cs
Normal file
175
SafeExamBrowser.Monitoring/System/Components/StickyKeys.cs
Normal file
|
@ -0,0 +1,175 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Threading.Tasks;
|
||||
using System.Timers;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.System.Components
|
||||
{
|
||||
internal class StickyKeys
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly INativeMethods nativeMethods;
|
||||
private readonly Timer timer;
|
||||
|
||||
private IStickyKeysState original;
|
||||
|
||||
internal event SentinelEventHandler Changed;
|
||||
|
||||
internal StickyKeys(ILogger logger, INativeMethods nativeMethods)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.timer = new Timer();
|
||||
}
|
||||
|
||||
internal bool Disable()
|
||||
{
|
||||
var success = nativeMethods.TryGetStickyKeys(out var state);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = nativeMethods.DisableStickyKeys();
|
||||
|
||||
if (success)
|
||||
{
|
||||
original = state;
|
||||
logger.Info($"Disabled sticky keys (original state: {ToString(state)}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to disable sticky keys (original state: {ToString(state)})!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to retrieve sticky keys state!");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
internal bool Enable()
|
||||
{
|
||||
var success = nativeMethods.TryGetStickyKeys(out var state);
|
||||
|
||||
if (success)
|
||||
{
|
||||
success = nativeMethods.EnableStickyKeys();
|
||||
|
||||
if (success)
|
||||
{
|
||||
original = state;
|
||||
logger.Info($"Enabled sticky keys (original state: {ToString(state)}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to enable sticky keys (original state: {ToString(state)})!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to retrieve sticky keys state!");
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
internal bool Revert()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
if (original != default)
|
||||
{
|
||||
success = nativeMethods.TrySetStickyKeys(original);
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info($"Successfully reverted sticky keys (original state: {ToString(original)}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to revert sticky keys (original state: {ToString(original)})!");
|
||||
}
|
||||
}
|
||||
|
||||
original = default;
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
internal void StartMonitoring()
|
||||
{
|
||||
timer.AutoReset = true;
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Interval = 1000;
|
||||
timer.Start();
|
||||
|
||||
logger.Info("Started monitoring sticky keys.");
|
||||
}
|
||||
|
||||
internal void StopMonitoring()
|
||||
{
|
||||
timer.Elapsed -= Timer_Elapsed;
|
||||
timer.Stop();
|
||||
|
||||
logger.Info("Stopped monitoring sticky keys.");
|
||||
}
|
||||
|
||||
private void Timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
if (nativeMethods.TryGetStickyKeys(out var state))
|
||||
{
|
||||
if (state.IsEnabled || state.IsHotkeyActive)
|
||||
{
|
||||
HandleStickyKeysChange(state);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to monitor sticky keys state!");
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleStickyKeysChange(IStickyKeysState state)
|
||||
{
|
||||
var args = new SentinelEventArgs();
|
||||
|
||||
logger.Warn($"The sticky keys state has changed: {ToString(state)}.");
|
||||
|
||||
Task.Run(() => Changed?.Invoke(args)).ContinueWith((_) =>
|
||||
{
|
||||
if (args.Allow)
|
||||
{
|
||||
StopMonitoring();
|
||||
}
|
||||
});
|
||||
|
||||
if (nativeMethods.DisableStickyKeys())
|
||||
{
|
||||
logger.Info($"Disabled sticky keys.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error($"Failed to disable sticky keys!");
|
||||
}
|
||||
}
|
||||
|
||||
private string ToString(IStickyKeysState state)
|
||||
{
|
||||
var availability = state.IsAvailable ? "available" : "unavailable";
|
||||
var status = state.IsEnabled ? "enabled" : "disabled";
|
||||
var hotkey = state.IsHotkeyActive ? "active" : "inactive";
|
||||
|
||||
return $"functionality {availability} and {status}, hotkey {hotkey}";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,46 +10,47 @@ using System;
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.Win32;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.System
|
||||
namespace SafeExamBrowser.Monitoring.System.Components
|
||||
{
|
||||
public class SystemMonitor : ISystemMonitor
|
||||
internal class SystemEvents
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public event SessionChangedEventHandler SessionChanged;
|
||||
internal event SessionChangedEventHandler SessionChanged;
|
||||
|
||||
public SystemMonitor(ILogger logger)
|
||||
internal SystemEvents(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public void Start()
|
||||
internal void StartMonitoring()
|
||||
{
|
||||
SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown;
|
||||
SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged;
|
||||
SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
SystemEvents.SessionEnded += SystemEvents_SessionEnded;
|
||||
SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
SystemEvents.SessionSwitch += SystemEvents_SessionChanged;
|
||||
SystemEvents.TimeChanged += SystemEvents_TimeChanged;
|
||||
SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
|
||||
logger.Info("Started monitoring the operating system.");
|
||||
Microsoft.Win32.SystemEvents.EventsThreadShutdown += SystemEvents_EventsThreadShutdown;
|
||||
Microsoft.Win32.SystemEvents.InstalledFontsChanged += SystemEvents_InstalledFontsChanged;
|
||||
Microsoft.Win32.SystemEvents.PowerModeChanged += SystemEvents_PowerModeChanged;
|
||||
Microsoft.Win32.SystemEvents.SessionEnded += SystemEvents_SessionEnded;
|
||||
Microsoft.Win32.SystemEvents.SessionEnding += SystemEvents_SessionEnding;
|
||||
Microsoft.Win32.SystemEvents.SessionSwitch += SystemEvents_SessionChanged;
|
||||
Microsoft.Win32.SystemEvents.TimeChanged += SystemEvents_TimeChanged;
|
||||
Microsoft.Win32.SystemEvents.UserPreferenceChanged += SystemEvents_UserPreferenceChanged;
|
||||
|
||||
logger.Info("Started monitoring system events.");
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
internal void StopMonitoring()
|
||||
{
|
||||
SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown;
|
||||
SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged;
|
||||
SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
||||
SystemEvents.SessionEnded -= SystemEvents_SessionEnded;
|
||||
SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
|
||||
SystemEvents.SessionSwitch -= SystemEvents_SessionChanged;
|
||||
SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
|
||||
SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
|
||||
logger.Info("Stopped monitoring the operating system.");
|
||||
Microsoft.Win32.SystemEvents.EventsThreadShutdown -= SystemEvents_EventsThreadShutdown;
|
||||
Microsoft.Win32.SystemEvents.InstalledFontsChanged -= SystemEvents_InstalledFontsChanged;
|
||||
Microsoft.Win32.SystemEvents.PowerModeChanged -= SystemEvents_PowerModeChanged;
|
||||
Microsoft.Win32.SystemEvents.SessionEnded -= SystemEvents_SessionEnded;
|
||||
Microsoft.Win32.SystemEvents.SessionEnding -= SystemEvents_SessionEnding;
|
||||
Microsoft.Win32.SystemEvents.SessionSwitch -= SystemEvents_SessionChanged;
|
||||
Microsoft.Win32.SystemEvents.TimeChanged -= SystemEvents_TimeChanged;
|
||||
Microsoft.Win32.SystemEvents.UserPreferenceChanged -= SystemEvents_UserPreferenceChanged;
|
||||
|
||||
logger.Info("Stopped monitoring system events.");
|
||||
}
|
||||
|
||||
private void SystemEvents_EventsThreadShutdown(object sender, EventArgs e)
|
95
SafeExamBrowser.Monitoring/System/SystemSentinel.cs
Normal file
95
SafeExamBrowser.Monitoring/System/SystemSentinel.cs
Normal file
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System.Events;
|
||||
using SafeExamBrowser.Monitoring.System.Components;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Monitoring.System
|
||||
{
|
||||
public class SystemSentinel : ISystemSentinel
|
||||
{
|
||||
private readonly Cursors cursors;
|
||||
private readonly EaseOfAccess easeOfAccess;
|
||||
private readonly StickyKeys stickyKeys;
|
||||
private readonly SystemEvents systemEvents;
|
||||
|
||||
public event SentinelEventHandler CursorChanged;
|
||||
public event SentinelEventHandler EaseOfAccessChanged;
|
||||
public event SentinelEventHandler StickyKeysChanged;
|
||||
public event SessionChangedEventHandler SessionChanged;
|
||||
|
||||
public SystemSentinel(ILogger logger, INativeMethods nativeMethods, IRegistry registry)
|
||||
{
|
||||
cursors = new Cursors(logger, registry);
|
||||
easeOfAccess = new EaseOfAccess(logger, registry);
|
||||
stickyKeys = new StickyKeys(logger, nativeMethods);
|
||||
systemEvents = new SystemEvents(logger);
|
||||
}
|
||||
|
||||
public bool DisableStickyKeys()
|
||||
{
|
||||
return stickyKeys.Disable();
|
||||
}
|
||||
|
||||
public bool EnableStickyKeys()
|
||||
{
|
||||
return stickyKeys.Enable();
|
||||
}
|
||||
|
||||
public bool RevertStickyKeys()
|
||||
{
|
||||
return stickyKeys.Revert();
|
||||
}
|
||||
|
||||
public void StartMonitoringCursors()
|
||||
{
|
||||
cursors.CursorChanged += (args) => CursorChanged?.Invoke(args);
|
||||
cursors.StartMonitoring();
|
||||
}
|
||||
|
||||
public void StartMonitoringEaseOfAccess()
|
||||
{
|
||||
easeOfAccess.EaseOfAccessChanged += (args) => EaseOfAccessChanged?.Invoke(args);
|
||||
easeOfAccess.StartMonitoring();
|
||||
}
|
||||
|
||||
public void StartMonitoringStickyKeys()
|
||||
{
|
||||
stickyKeys.Changed += (args) => StickyKeysChanged?.Invoke(args);
|
||||
stickyKeys.StartMonitoring();
|
||||
}
|
||||
|
||||
public void StartMonitoringSystemEvents()
|
||||
{
|
||||
systemEvents.SessionChanged += () => SessionChanged?.Invoke();
|
||||
systemEvents.StartMonitoring();
|
||||
}
|
||||
|
||||
public void StopMonitoring()
|
||||
{
|
||||
cursors.StopMonitoring();
|
||||
easeOfAccess.StopMonitoring();
|
||||
stickyKeys.StopMonitoring();
|
||||
systemEvents.StopMonitoring();
|
||||
}
|
||||
|
||||
public bool VerifyCursors()
|
||||
{
|
||||
return cursors.Verify();
|
||||
}
|
||||
|
||||
public bool VerifyEaseOfAccess()
|
||||
{
|
||||
return easeOfAccess.Verify();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,12 +6,14 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
|
||||
namespace SafeExamBrowser.SystemComponents
|
||||
namespace SafeExamBrowser.Monitoring
|
||||
{
|
||||
public class VirtualMachineDetector : IVirtualMachineDetector
|
||||
{
|
||||
|
@ -105,7 +107,6 @@ namespace SafeExamBrowser.SystemComponents
|
|||
{
|
||||
var isVirtualRegistry = false;
|
||||
|
||||
isVirtualRegistry |= HasHistoricVirtualMachineHardwareConfiguration();
|
||||
isVirtualRegistry |= HasLocalVirtualMachineDeviceCache();
|
||||
|
||||
return isVirtualRegistry;
|
||||
|
@ -134,49 +135,9 @@ namespace SafeExamBrowser.SystemComponents
|
|||
return isVirtualSystem;
|
||||
}
|
||||
|
||||
private bool HasHistoricVirtualMachineHardwareConfiguration()
|
||||
{
|
||||
var hasHistoricConfiguration = false;
|
||||
|
||||
if (registry.TryGetSubKeys(RegistryValue.MachineHive.HardwareConfig_Key, out var hardwareConfigSubkeys))
|
||||
{
|
||||
foreach (var configId in hardwareConfigSubkeys)
|
||||
{
|
||||
var hardwareConfigKey = $@"{RegistryValue.MachineHive.HardwareConfig_Key}\{configId}";
|
||||
var computerIdsKey = $@"{hardwareConfigKey}\ComputerIds";
|
||||
var success = true;
|
||||
|
||||
success &= registry.TryRead(hardwareConfigKey, "BIOSVendor", out var biosVendor);
|
||||
success &= registry.TryRead(hardwareConfigKey, "BIOSVersion", out var biosVersion);
|
||||
success &= registry.TryRead(hardwareConfigKey, "SystemManufacturer", out var systemManufacturer);
|
||||
success &= registry.TryRead(hardwareConfigKey, "SystemProductName", out var systemProductName);
|
||||
|
||||
if (success)
|
||||
{
|
||||
var biosInfo = $"{(string) biosVendor} {(string) biosVersion}";
|
||||
|
||||
hasHistoricConfiguration |= IsVirtualSystem(biosInfo, (string) systemManufacturer, (string) systemProductName);
|
||||
|
||||
if (registry.TryGetNames(computerIdsKey, out var computerIdNames))
|
||||
{
|
||||
foreach (var computerIdName in computerIdNames)
|
||||
{
|
||||
if (registry.TryRead(computerIdsKey, computerIdName, out var computerSummary))
|
||||
{
|
||||
hasHistoricConfiguration |= IsVirtualSystem((string) computerSummary, (string) systemManufacturer, (string) systemProductName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return hasHistoricConfiguration;
|
||||
}
|
||||
|
||||
private bool HasLocalVirtualMachineDeviceCache()
|
||||
{
|
||||
var deviceName = System.Environment.GetEnvironmentVariable("COMPUTERNAME");
|
||||
var deviceName = Environment.GetEnvironmentVariable("COMPUTERNAME");
|
||||
var hasDeviceCache = false;
|
||||
var hasDeviceCacheKeys = registry.TryGetSubKeys(RegistryValue.UserHive.DeviceCache_Key, out var deviceCacheKeys);
|
||||
|
|
@ -1,255 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.IO;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Windows;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.Resources.Icons;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts.Events.Proctoring;
|
||||
using SafeExamBrowser.Settings.Proctoring;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||
|
||||
namespace SafeExamBrowser.Proctoring.JitsiMeet
|
||||
{
|
||||
internal class JitsiMeetImplementation : ProctoringImplementation
|
||||
{
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly IFileSystem fileSystem;
|
||||
private readonly IModuleLogger logger;
|
||||
private readonly ProctoringSettings settings;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private ProctoringControl control;
|
||||
private string filePath;
|
||||
private WindowVisibility initialVisibility;
|
||||
private IProctoringWindow window;
|
||||
|
||||
internal override string Name => nameof(JitsiMeet);
|
||||
|
||||
internal JitsiMeetImplementation(
|
||||
AppConfig appConfig,
|
||||
IFileSystem fileSystem,
|
||||
IModuleLogger logger,
|
||||
ProctoringSettings settings,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.fileSystem = fileSystem;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
internal override void Initialize()
|
||||
{
|
||||
var start = true;
|
||||
|
||||
initialVisibility = settings.WindowVisibility;
|
||||
settings.JitsiMeet.ServerUrl = Sanitize(settings.JitsiMeet.ServerUrl);
|
||||
|
||||
start &= !string.IsNullOrWhiteSpace(settings.JitsiMeet.RoomName);
|
||||
start &= !string.IsNullOrWhiteSpace(settings.JitsiMeet.ServerUrl);
|
||||
|
||||
if (start)
|
||||
{
|
||||
logger.Info($"Initialized proctoring: All settings are valid, starting automatically...");
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowNotificationInactive();
|
||||
logger.Info($"Initialized proctoring: Not all settings are valid or a server session is active, not starting automatically.");
|
||||
}
|
||||
}
|
||||
|
||||
internal override void ProctoringConfigurationReceived(bool allowChat, bool receiveAudio, bool receiveVideo)
|
||||
{
|
||||
logger.Info("Proctoring configuration received.");
|
||||
|
||||
settings.JitsiMeet.AllowChat = allowChat;
|
||||
settings.JitsiMeet.ReceiveAudio = receiveAudio;
|
||||
settings.JitsiMeet.ReceiveVideo = receiveVideo;
|
||||
|
||||
if (allowChat || receiveVideo)
|
||||
{
|
||||
settings.WindowVisibility = WindowVisibility.AllowToHide;
|
||||
}
|
||||
else
|
||||
{
|
||||
settings.WindowVisibility = initialVisibility;
|
||||
}
|
||||
|
||||
Stop();
|
||||
Start();
|
||||
|
||||
logger.Info($"Successfully updated configuration: {nameof(allowChat)}={allowChat}, {nameof(receiveAudio)}={receiveAudio}, {nameof(receiveVideo)}={receiveVideo}.");
|
||||
}
|
||||
|
||||
internal override void ProctoringInstructionReceived(InstructionEventArgs args)
|
||||
{
|
||||
if (args is JitsiMeetInstruction instruction)
|
||||
{
|
||||
logger.Info($"Proctoring instruction received: {instruction.Method}");
|
||||
|
||||
if (instruction.Method == InstructionMethod.Join)
|
||||
{
|
||||
settings.JitsiMeet.RoomName = instruction.RoomName;
|
||||
settings.JitsiMeet.ServerUrl = instruction.ServerUrl;
|
||||
settings.JitsiMeet.Token = instruction.Token;
|
||||
|
||||
Stop();
|
||||
Start();
|
||||
}
|
||||
else
|
||||
{
|
||||
Stop();
|
||||
}
|
||||
|
||||
logger.Info("Successfully processed instruction.");
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Start()
|
||||
{
|
||||
Application.Current.Dispatcher.Invoke(() =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var content = LoadContent(settings);
|
||||
|
||||
filePath = Path.Combine(appConfig.TemporaryDirectory, $"{Path.GetRandomFileName()}_index.html");
|
||||
fileSystem.Save(content, filePath);
|
||||
|
||||
control = new ProctoringControl(logger.CloneFor(nameof(ProctoringControl)), settings);
|
||||
control.CreationProperties = new CoreWebView2CreationProperties { UserDataFolder = appConfig.TemporaryDirectory };
|
||||
control.EnsureCoreWebView2Async().ContinueWith(_ =>
|
||||
{
|
||||
control.Dispatcher.Invoke(() =>
|
||||
{
|
||||
control.CoreWebView2.Navigate(filePath);
|
||||
});
|
||||
});
|
||||
|
||||
window = uiFactory.CreateProctoringWindow(control);
|
||||
window.SetTitle(settings.JitsiMeet.Enabled ? settings.JitsiMeet.Subject : "");
|
||||
window.Show();
|
||||
|
||||
if (settings.WindowVisibility == WindowVisibility.AllowToShow || settings.WindowVisibility == WindowVisibility.Hidden)
|
||||
{
|
||||
window.Hide();
|
||||
}
|
||||
|
||||
ShowNotificationActive();
|
||||
|
||||
logger.Info("Started proctoring.");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to start proctoring! Reason: {e.Message}", e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
internal override void Stop()
|
||||
{
|
||||
if (control != default && window != default)
|
||||
{
|
||||
control.Dispatcher.Invoke(() =>
|
||||
{
|
||||
control.ExecuteScriptAsync("api.executeCommand('hangup'); api.dispose();");
|
||||
|
||||
Thread.Sleep(2000);
|
||||
|
||||
window.Close();
|
||||
control = default;
|
||||
window = default;
|
||||
fileSystem.Delete(filePath);
|
||||
|
||||
ShowNotificationInactive();
|
||||
|
||||
logger.Info("Stopped proctoring.");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
internal override void Terminate()
|
||||
{
|
||||
Stop();
|
||||
logger.Info("Terminated proctoring.");
|
||||
}
|
||||
|
||||
protected override void ActivateNotification()
|
||||
{
|
||||
if (settings.WindowVisibility == WindowVisibility.Visible)
|
||||
{
|
||||
window?.BringToForeground();
|
||||
}
|
||||
else if (settings.WindowVisibility == WindowVisibility.AllowToHide || settings.WindowVisibility == WindowVisibility.AllowToShow)
|
||||
{
|
||||
window?.Toggle();
|
||||
}
|
||||
}
|
||||
|
||||
private string LoadContent(ProctoringSettings settings)
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(ProctoringController));
|
||||
var path = $"{typeof(ProctoringController).Namespace}.JitsiMeet.index.html";
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(path))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
var html = reader.ReadToEnd();
|
||||
|
||||
html = html.Replace("%%_ALLOW_CHAT_%%", settings.JitsiMeet.AllowChat ? "chat" : "");
|
||||
html = html.Replace("%%_ALLOW_CLOSED_CAPTIONS_%%", settings.JitsiMeet.AllowClosedCaptions ? "closedcaptions" : "");
|
||||
html = html.Replace("%%_ALLOW_RAISE_HAND_%%", settings.JitsiMeet.AllowRaiseHand ? "raisehand" : "");
|
||||
html = html.Replace("%%_ALLOW_RECORDING_%%", settings.JitsiMeet.AllowRecording ? "recording" : "");
|
||||
html = html.Replace("%%_ALLOW_TILE_VIEW", settings.JitsiMeet.AllowTileView ? "tileview" : "");
|
||||
html = html.Replace("'%_AUDIO_MUTED_%'", settings.JitsiMeet.AudioMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
html = html.Replace("'%_AUDIO_ONLY_%'", settings.JitsiMeet.AudioOnly ? "true" : "false");
|
||||
html = html.Replace("'%_VIDEO_MUTED_%'", settings.JitsiMeet.VideoMuted && settings.WindowVisibility != WindowVisibility.Hidden ? "true" : "false");
|
||||
|
||||
return html;
|
||||
}
|
||||
}
|
||||
|
||||
private string Sanitize(string serverUrl)
|
||||
{
|
||||
return serverUrl?.Replace($"{Uri.UriSchemeHttp}{Uri.SchemeDelimiter}", "").Replace($"{Uri.UriSchemeHttps}{Uri.SchemeDelimiter}", "");
|
||||
}
|
||||
|
||||
private void ShowNotificationActive()
|
||||
{
|
||||
CanActivate = true;
|
||||
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Proctoring_Active.xaml") };
|
||||
Tooltip = text.Get(TextKey.Notification_ProctoringActiveTooltip);
|
||||
|
||||
InvokeNotificationChanged();
|
||||
}
|
||||
|
||||
private void ShowNotificationInactive()
|
||||
{
|
||||
CanActivate = false;
|
||||
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/Proctoring_Inactive.xaml") };
|
||||
Tooltip = text.Get(TextKey.Notification_ProctoringInactiveTooltip);
|
||||
|
||||
InvokeNotificationChanged();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,69 +0,0 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
</head>
|
||||
<body style="margin: 0">
|
||||
<div id="placeholder" />
|
||||
<script src='https://meet.jit.si/external_api.js'></script>
|
||||
<script type="text/javascript">
|
||||
var api;
|
||||
|
||||
function startMeeting(credentials) {
|
||||
var configOverwrite = {
|
||||
disableProfile: true,
|
||||
prejoinPageEnabled: false,
|
||||
startAudioOnly: '%_AUDIO_ONLY_%',
|
||||
startWithAudioMuted: '%_AUDIO_MUTED_%',
|
||||
startWithVideoMuted: '%_VIDEO_MUTED_%'
|
||||
};
|
||||
var interfaceOverwrite = {
|
||||
JITSI_WATERMARK_LINK: '',
|
||||
SHOW_JITSI_WATERMARK: false,
|
||||
TOOLBAR_BUTTONS: [
|
||||
'microphone', 'camera', '%%_ALLOW_CLOSED_CAPTIONS_%%', /*'desktop',*/ 'embedmeeting', 'fullscreen',
|
||||
'fodeviceselection', /*'hangup',*/ 'profile', '%%_ALLOW_CHAT_%%', '%%_ALLOW_RECORDING_%%',
|
||||
'livestreaming', 'etherpad', /*'sharedvideo',*/ 'settings', '%%_ALLOW_RAISE_HAND_%%',
|
||||
'videoquality', 'filmstrip', 'invite', 'feedback', 'stats', 'shortcuts',
|
||||
'%%_ALLOW_TILE_VIEW_%%', 'select-background', 'download', 'help', /*'mute-everyone',*/ 'mute-video-everyone', 'security'
|
||||
]
|
||||
};
|
||||
var options = {
|
||||
configOverwrite: configOverwrite,
|
||||
height: '100%',
|
||||
interfaceConfigOverwrite: interfaceOverwrite,
|
||||
jwt: credentials.token,
|
||||
parentNode: document.querySelector('#placeholder'),
|
||||
roomName: credentials.roomName,
|
||||
width: '100%'
|
||||
};
|
||||
|
||||
api = new JitsiMeetExternalAPI(credentials.domain, options);
|
||||
api.addListener('audioMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleAudio');
|
||||
}
|
||||
});
|
||||
api.addListener('videoMuteStatusChanged', args => {
|
||||
if (args.muted) {
|
||||
api.executeCommand('toggleVideo');
|
||||
}
|
||||
});
|
||||
|
||||
api.executeCommand('subject', credentials.subject);
|
||||
}
|
||||
|
||||
function webMessageReceived(args) {
|
||||
if ('credentials' in args.data) {
|
||||
startMeeting(args.data.credentials);
|
||||
}
|
||||
}
|
||||
|
||||
window.addEventListener('unload', () => {
|
||||
api.executeCommand('hangup');
|
||||
api.dispose();
|
||||
});
|
||||
window.chrome.webview.addEventListener('message', webMessageReceived);
|
||||
window.chrome.webview.postMessage('credentials');
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -1,104 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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 Microsoft.Web.WebView2.Core;
|
||||
using Microsoft.Web.WebView2.Wpf;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Proctoring;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
|
||||
|
||||
namespace SafeExamBrowser.Proctoring
|
||||
{
|
||||
internal class ProctoringControl : WebView2, IProctoringControl
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly ProctoringSettings settings;
|
||||
|
||||
public event FullScreenChangedEventHandler FullScreenChanged;
|
||||
|
||||
internal ProctoringControl(ILogger logger, ProctoringSettings settings)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
|
||||
CoreWebView2InitializationCompleted += ProctoringControl_CoreWebView2InitializationCompleted;
|
||||
}
|
||||
|
||||
private void ProctoringControl_CoreWebView2InitializationCompleted(object sender, CoreWebView2InitializationCompletedEventArgs e)
|
||||
{
|
||||
if (e.IsSuccess)
|
||||
{
|
||||
CoreWebView2.Settings.AreDefaultContextMenusEnabled = false;
|
||||
CoreWebView2.Settings.AreDevToolsEnabled = false;
|
||||
CoreWebView2.Settings.IsStatusBarEnabled = false;
|
||||
CoreWebView2.ContainsFullScreenElementChanged += CoreWebView2_ContainsFullScreenElementChanged;
|
||||
CoreWebView2.PermissionRequested += CoreWebView2_PermissionRequested;
|
||||
CoreWebView2.WebMessageReceived += CoreWebView2_WebMessageReceived;
|
||||
logger.Info("Successfully initialized.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to initialize!", e.InitializationException);
|
||||
}
|
||||
}
|
||||
|
||||
private void CoreWebView2_ContainsFullScreenElementChanged(object sender, object e)
|
||||
{
|
||||
FullScreenChanged?.Invoke(CoreWebView2.ContainsFullScreenElement);
|
||||
logger.Debug($"Full screen {(CoreWebView2.ContainsFullScreenElement ? "activated" : "deactivated")}.");
|
||||
}
|
||||
|
||||
private void CoreWebView2_PermissionRequested(object sender, CoreWebView2PermissionRequestedEventArgs e)
|
||||
{
|
||||
if (e.PermissionKind == CoreWebView2PermissionKind.Camera || e.PermissionKind == CoreWebView2PermissionKind.Microphone)
|
||||
{
|
||||
e.State = CoreWebView2PermissionState.Allow;
|
||||
logger.Info($"Granted access to {e.PermissionKind}.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info($"Denied access to {e.PermissionKind}.");
|
||||
}
|
||||
}
|
||||
|
||||
private void CoreWebView2_WebMessageReceived(object sender, CoreWebView2WebMessageReceivedEventArgs e)
|
||||
{
|
||||
var message = e.TryGetWebMessageAsString();
|
||||
|
||||
logger.Debug($"Received web message '{message}'.");
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case "credentials":
|
||||
SendCredentials();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendCredentials()
|
||||
{
|
||||
var message = new JObject();
|
||||
var credentials = new JObject();
|
||||
|
||||
if (settings.JitsiMeet.Enabled)
|
||||
{
|
||||
credentials.Add(new JProperty("domain", settings.JitsiMeet.ServerUrl));
|
||||
credentials.Add(new JProperty("roomName", settings.JitsiMeet.RoomName));
|
||||
credentials.Add(new JProperty("subject", settings.JitsiMeet.ShowMeetingName ? settings.JitsiMeet.Subject : ""));
|
||||
credentials.Add(new JProperty("token", settings.JitsiMeet.Token));
|
||||
}
|
||||
|
||||
message.Add("credentials", credentials);
|
||||
logger.Debug("Sending credentials to proctoring client.");
|
||||
|
||||
CoreWebView2.PostWebMessageAsJson(message.ToString());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,7 +12,6 @@ using SafeExamBrowser.Configuration.Contracts;
|
|||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
using SafeExamBrowser.Proctoring.JitsiMeet;
|
||||
using SafeExamBrowser.Proctoring.ScreenProctoring;
|
||||
using SafeExamBrowser.Proctoring.ScreenProctoring.Service;
|
||||
using SafeExamBrowser.Settings.Proctoring;
|
||||
|
@ -57,13 +56,6 @@ namespace SafeExamBrowser.Proctoring
|
|||
{
|
||||
var implementations = new List<ProctoringImplementation>();
|
||||
|
||||
if (settings.JitsiMeet.Enabled)
|
||||
{
|
||||
var logger = this.logger.CloneFor(nameof(JitsiMeet));
|
||||
|
||||
implementations.Add(new JitsiMeetImplementation(appConfig, fileSystem, logger, settings, text, uiFactory));
|
||||
}
|
||||
|
||||
if (settings.ScreenProctoring.Enabled)
|
||||
{
|
||||
var logger = this.logger.CloneFor(nameof(ScreenProctoring));
|
||||
|
|
|
@ -62,15 +62,6 @@
|
|||
<Reference Include="KGySoft.Drawing.Core, Version=8.1.0.0, Culture=neutral, PublicKeyToken=b45eba277439ddfe, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\KGySoft.Drawing.Core.8.1.0\lib\net46\KGySoft.Drawing.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Core, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.WinForms, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.WinForms.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Web.WebView2.Wpf, Version=1.0.2365.46, Culture=neutral, PublicKeyToken=2a8ab48044d2601e, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Web.WebView2.1.0.2365.46\lib\net45\Microsoft.Web.WebView2.Wpf.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Newtonsoft.Json, Version=13.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Newtonsoft.Json.13.0.3\lib\net45\Newtonsoft.Json.dll</HintPath>
|
||||
</Reference>
|
||||
|
@ -87,8 +78,6 @@
|
|||
<Reference Include="WindowsBase" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="JitsiMeet\JitsiMeetImplementation.cs" />
|
||||
<Compile Include="ProctoringControl.cs" />
|
||||
<Compile Include="ProctoringController.cs" />
|
||||
<Compile Include="ProctoringFactory.cs" />
|
||||
<Compile Include="ProctoringImplementation.cs" />
|
||||
|
@ -176,19 +165,9 @@
|
|||
<Name>SafeExamBrowser.WindowsApi.Contracts</Name>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="JitsiMeet\index.html" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets" Condition="Exists('..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets')" />
|
||||
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
|
||||
<PropertyGroup>
|
||||
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
|
||||
</PropertyGroup>
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Web.WebView2.1.0.2365.46\build\Microsoft.Web.WebView2.targets'))" />
|
||||
</Target>
|
||||
</Project>
|
|
@ -26,7 +26,6 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data
|
|||
|
||||
private string applicationInfo;
|
||||
private string browserInfo;
|
||||
private string browserInfoWithoutUrls;
|
||||
private TimeSpan elapsed;
|
||||
private string triggerInfo;
|
||||
private string urls;
|
||||
|
@ -76,7 +75,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data
|
|||
CaptureMouseTrigger(mouse);
|
||||
}
|
||||
|
||||
logger.Debug($"Captured metadata: {applicationInfo} / {browserInfoWithoutUrls} / {triggerInfo} / {urlCount} URL(s) / {windowTitle}.");
|
||||
logger.Debug($"Captured metadata: {applicationInfo} / {browserInfo} / {urlCount} URL(s) / {triggerInfo} / {windowTitle}.");
|
||||
}
|
||||
|
||||
private void CaptureApplicationData()
|
||||
|
@ -101,8 +100,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data
|
|||
{
|
||||
var windows = browser.GetWindows();
|
||||
|
||||
browserInfo = string.Join(", ", windows.Select(w => $"{(w.IsMainWindow ? "Main" : "Additional")} Window: {w.Title} ({w.Url})"));
|
||||
browserInfoWithoutUrls = string.Join(", ", windows.Select(w => $"{(w.IsMainWindow ? "Main" : "Additional")} Window: {w.Title}"));
|
||||
browserInfo = string.Join(", ", windows.Select(w => $"{(w.IsMainWindow ? "Main" : "Additional")} Window: {w.Title}"));
|
||||
urls = string.Join(", ", windows.Select(w => w.Url));
|
||||
urlCount = windows.Count();
|
||||
}
|
||||
|
@ -115,10 +113,19 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data
|
|||
|
||||
private void CaptureKeyboardTrigger(KeyboardTrigger keyboard)
|
||||
{
|
||||
var flags = Enum.GetValues(typeof(KeyModifier)).OfType<KeyModifier>().Where(m => m != KeyModifier.None && keyboard.Modifier.HasFlag(m));
|
||||
var flags = Enum.GetValues(typeof(KeyModifier))
|
||||
.OfType<KeyModifier>()
|
||||
.Where(m => m != KeyModifier.None && keyboard.Modifier.HasFlag(m) && !keyboard.Key.ToString().Contains(m.ToString()));
|
||||
var modifiers = flags.Any() ? string.Join(" + ", flags) + " + " : string.Empty;
|
||||
|
||||
triggerInfo = $"'{modifiers}{keyboard.Key}' has been {keyboard.State.ToString().ToLower()}.";
|
||||
if (flags.Any())
|
||||
{
|
||||
triggerInfo = $"'{modifiers}{keyboard.Key}' has been {keyboard.State.ToString().ToLower()}.";
|
||||
}
|
||||
else
|
||||
{
|
||||
triggerInfo = $"A key has been {keyboard.State.ToString().ToLower()}.";
|
||||
}
|
||||
}
|
||||
|
||||
private void CaptureMouseTrigger(MouseTrigger mouse)
|
||||
|
@ -156,7 +163,6 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Data
|
|||
{
|
||||
applicationInfo = "-";
|
||||
browserInfo = "-";
|
||||
browserInfoWithoutUrls = "-";
|
||||
triggerInfo = "-";
|
||||
urls = "-";
|
||||
windowTitle = "-";
|
||||
|
|
|
@ -166,7 +166,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
{
|
||||
logger.Info("Connecting to service...");
|
||||
|
||||
var connect = service.Connect(settings.ServiceUrl);
|
||||
var connect = service.Connect(settings.ClientId, settings.ClientSecret, settings.ServiceUrl);
|
||||
|
||||
if (connect.Success)
|
||||
{
|
||||
|
@ -191,11 +191,11 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
}
|
||||
}
|
||||
|
||||
private void UpdateNotification(bool live)
|
||||
private void UpdateNotification(bool active)
|
||||
{
|
||||
CanActivate = false;
|
||||
|
||||
if (live)
|
||||
if (active)
|
||||
{
|
||||
IconResource = new XamlIconResource { Uri = new Uri("pack://application:,,,/SafeExamBrowser.UserInterface.Desktop;component/Images/ScreenProctoring_Active.xaml") };
|
||||
Tooltip = text.Get(TextKey.Notification_ProctoringActiveTooltip);
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
||||
{
|
||||
|
@ -15,7 +16,20 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
{
|
||||
internal static string ToLogString(this HttpResponseMessage response)
|
||||
{
|
||||
return $"{(int?) response?.StatusCode} {response?.StatusCode} {response?.ReasonPhrase}";
|
||||
return response == default ? "No Response" : $"{(int) response.StatusCode} {response.StatusCode} {response.ReasonPhrase}";
|
||||
}
|
||||
|
||||
internal static string ToSummary(this Exception exception)
|
||||
{
|
||||
var trimChars = new[] { '.', '!' };
|
||||
var summary = new StringBuilder(exception.Message?.TrimEnd(trimChars));
|
||||
|
||||
for (var inner = exception.InnerException; inner != default; inner = inner.InnerException)
|
||||
{
|
||||
summary.Append($" -> {inner.Message?.TrimEnd(trimChars)}");
|
||||
}
|
||||
|
||||
return summary.ToString();
|
||||
}
|
||||
|
||||
internal static long ToUnixTimestamp(this DateTime date)
|
||||
|
|
|
@ -17,8 +17,11 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
{
|
||||
}
|
||||
|
||||
internal bool TryExecute(out string message)
|
||||
internal bool TryExecute(string clientId, string clientSecret, out string message)
|
||||
{
|
||||
ClientId = clientId;
|
||||
ClientSecret = clientSecret;
|
||||
|
||||
return TryRetrieveOAuth2Token(out message);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,29 +21,21 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
{
|
||||
private const int ATTEMPTS = 5;
|
||||
|
||||
private static string connectionToken;
|
||||
private static string oauth2Token;
|
||||
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private bool hadException;
|
||||
|
||||
protected readonly Api api;
|
||||
protected readonly ILogger logger;
|
||||
protected readonly Parser parser;
|
||||
|
||||
protected static string ClientId { get; set; }
|
||||
protected static string ClientSecret { get; set; }
|
||||
|
||||
protected (string, string) Authorization => (Header.AUTHORIZATION, $"Bearer {oauth2Token}");
|
||||
|
||||
internal static string ConnectionToken
|
||||
{
|
||||
get { return connectionToken; }
|
||||
set { connectionToken = value; }
|
||||
}
|
||||
|
||||
internal static string Oauth2Token
|
||||
{
|
||||
get { return oauth2Token; }
|
||||
set { oauth2Token = value; }
|
||||
}
|
||||
|
||||
protected Request(Api api, HttpClient httpClient, ILogger logger, Parser parser)
|
||||
{
|
||||
this.api = api;
|
||||
|
@ -84,12 +76,16 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
}
|
||||
catch (TaskCanceledException)
|
||||
{
|
||||
logger.Error($"Request {request.Method} '{request.RequestUri}' did not complete within {httpClient.Timeout}ms!");
|
||||
logger.Warn($"Request {request.Method} '{request.RequestUri}' did not complete within {httpClient.Timeout}!");
|
||||
|
||||
break;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Request {request.Method} '{request.RequestUri}' has failed!", e);
|
||||
if (IsFirstException())
|
||||
{
|
||||
logger.Warn($"Request {request.Method} '{request.RequestUri}' has failed: {e.ToSummary()}!");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -98,7 +94,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
|
||||
protected bool TryRetrieveOAuth2Token(out string message)
|
||||
{
|
||||
var secret = Convert.ToBase64String(Encoding.UTF8.GetBytes("test:test"));
|
||||
var secret = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{ClientId}:{ClientSecret}"));
|
||||
var authorization = (Header.AUTHORIZATION, $"Basic {secret}");
|
||||
var content = "grant_type=client_credentials&scope=read write";
|
||||
var success = TryExecute(HttpMethod.Post, api.AccessTokenEndpoint, out var response, content, ContentType.URL_ENCODED, authorization);
|
||||
|
@ -154,6 +150,15 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
return request;
|
||||
}
|
||||
|
||||
private bool IsFirstException()
|
||||
{
|
||||
var isFirst = !hadException;
|
||||
|
||||
hadException = true;
|
||||
|
||||
return isFirst;
|
||||
}
|
||||
|
||||
private (string name, string value)[] UpdateOAuth2Token((string name, string value)[] headers)
|
||||
{
|
||||
var result = new List<(string name, string value)>();
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Proctoring.ScreenProctoring.Data;
|
||||
|
@ -24,8 +25,8 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service.Requests
|
|||
internal bool TryExecute(MetaData metaData, ScreenShot screenShot, string sessionId, out int health, out string message)
|
||||
{
|
||||
var imageFormat = (Header.IMAGE_FORMAT, ToString(screenShot.Format));
|
||||
var metdataJson = (Header.METADATA, metaData.ToJson());
|
||||
var timestamp = (Header.TIMESTAMP, DateTime.Now.ToUnixTimestamp().ToString());
|
||||
var metdataJson = (Header.METADATA, WebUtility.UrlEncode(metaData.ToJson()));
|
||||
var timestamp = (Header.TIMESTAMP, screenShot.CaptureTime.ToUnixTimestamp().ToString());
|
||||
var url = api.ScreenShotEndpoint.Replace(Api.SESSION_ID, sessionId);
|
||||
var success = TryExecute(HttpMethod.Post, url, out var response, screenShot.Data, ContentType.OCTET_STREAM, Authorization, imageFormat, metdataJson, timestamp);
|
||||
|
||||
|
|
|
@ -33,12 +33,16 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service
|
|||
this.parser = new Parser(logger);
|
||||
}
|
||||
|
||||
internal ServiceResponse Connect(string serviceUrl)
|
||||
internal ServiceResponse Connect(string clientId, string clientSecret, string serviceUrl)
|
||||
{
|
||||
httpClient = new HttpClient { BaseAddress = new Uri(serviceUrl) };
|
||||
httpClient = new HttpClient
|
||||
{
|
||||
BaseAddress = new Uri(serviceUrl),
|
||||
Timeout = TimeSpan.FromSeconds(10)
|
||||
};
|
||||
|
||||
var request = new OAuth2TokenRequest(api, httpClient, logger, parser);
|
||||
var success = request.TryExecute(out var message);
|
||||
var success = request.TryExecute(clientId, clientSecret, out var message);
|
||||
|
||||
if (success)
|
||||
{
|
||||
|
@ -81,7 +85,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring.Service
|
|||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to query health!");
|
||||
logger.Warn("Failed to query health!");
|
||||
}
|
||||
|
||||
return new ServiceResponse<int>(success, health, message);
|
||||
|
|
|
@ -62,7 +62,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
var progress = 0;
|
||||
var total = previous;
|
||||
|
||||
while (HasRemainingWork() && service.IsConnected && (!networkIssue || recovering))
|
||||
while (HasRemainingWork() && service.IsConnected && (!networkIssue || recovering || DateTime.Now < resume))
|
||||
{
|
||||
var remaining = buffer.Count + cache.Count;
|
||||
|
||||
|
@ -80,7 +80,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
|
||||
updateStatus(new RemainingWorkUpdatedEventArgs
|
||||
{
|
||||
IsWaiting = recovering,
|
||||
IsWaiting = recovering || networkIssue,
|
||||
Next = buffer.TryPeek(out _, out var schedule, out _) ? schedule : default(DateTime?),
|
||||
Progress = progress,
|
||||
Resume = resume,
|
||||
|
@ -121,6 +121,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
thread.Start();
|
||||
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Interval = FIFTEEN_SECONDS;
|
||||
}
|
||||
|
||||
|
@ -195,19 +196,6 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
|
||||
private void ExecuteCaching()
|
||||
{
|
||||
const int THREE_MINUTES = 180;
|
||||
|
||||
if (!recovering)
|
||||
{
|
||||
recovering = true;
|
||||
resume = DateTime.Now.AddSeconds(random.Next(0, THREE_MINUTES));
|
||||
|
||||
timer.Elapsed += Timer_Elapsed;
|
||||
timer.Start();
|
||||
|
||||
logger.Warn($"Activating local caching and suspending transmission due to bad service health (value: {health}, resume: {resume:HH:mm:ss}).");
|
||||
}
|
||||
|
||||
CacheFromBuffer();
|
||||
CacheFromQueue();
|
||||
}
|
||||
|
@ -237,9 +225,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
else
|
||||
{
|
||||
timer.Stop();
|
||||
timer.Elapsed -= Timer_Elapsed;
|
||||
|
||||
logger.Info($"Deactivating local caching and resuming transmission due to improved service health (value: {health}).");
|
||||
logger.Info($"Recovery terminated, deactivating local caching and resuming transmission.");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -278,7 +264,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
}
|
||||
else
|
||||
{
|
||||
buffer.Enqueue(metaData, DateTime.Now, screenShot);
|
||||
buffer.Enqueue(metaData, CalculateSchedule(metaData), screenShot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -296,10 +282,18 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
var hasItem = buffer.TryPeek(out var metaData, out var schedule, out var screenShot);
|
||||
var ready = schedule <= DateTime.Now;
|
||||
|
||||
if (hasItem && ready && TryTransmit(metaData, screenShot))
|
||||
if (hasItem && ready)
|
||||
{
|
||||
buffer.Dequeue();
|
||||
screenShot.Dispose();
|
||||
|
||||
if (TryTransmit(metaData, screenShot))
|
||||
{
|
||||
screenShot.Dispose();
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer.Enqueue(metaData, CalculateSchedule(metaData), screenShot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -313,7 +307,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
}
|
||||
else
|
||||
{
|
||||
buffer.Enqueue(metaData, DateTime.Now, screenShot);
|
||||
buffer.Enqueue(metaData, CalculateSchedule(metaData), screenShot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -328,7 +322,7 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
}
|
||||
else
|
||||
{
|
||||
buffer.Enqueue(metaData, DateTime.Now, screenShot);
|
||||
buffer.Enqueue(metaData, CalculateSchedule(metaData), screenShot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -354,14 +348,11 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
if (service.IsConnected)
|
||||
{
|
||||
var response = service.Send(metaData, screenShot);
|
||||
var value = response.Success ? response.Value : BAD;
|
||||
|
||||
health = UpdateHealth(value);
|
||||
networkIssue = !response.Success;
|
||||
success = response.Success;
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
health = UpdateHealth(response.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -376,30 +367,52 @@ namespace SafeExamBrowser.Proctoring.ScreenProctoring
|
|||
if (service.IsConnected)
|
||||
{
|
||||
var response = service.GetHealth();
|
||||
var value = response.Success ? response.Value : BAD;
|
||||
|
||||
health = UpdateHealth(value);
|
||||
networkIssue = !response.Success;
|
||||
|
||||
if (response.Success)
|
||||
{
|
||||
health = UpdateHealth(response.Value);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Cannot query health as service is disconnected!");
|
||||
}
|
||||
|
||||
timer.Start();
|
||||
if (!timer.Enabled)
|
||||
{
|
||||
timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private int UpdateHealth(int value)
|
||||
{
|
||||
const int THREE_MINUTES = 180;
|
||||
|
||||
var previous = health;
|
||||
var current = value > BAD ? BAD : (value < GOOD ? GOOD : value);
|
||||
|
||||
if (previous != current)
|
||||
{
|
||||
logger.Info($"Service health {(previous < current ? "deteriorated" : "improved")} from {previous} to {current}.");
|
||||
|
||||
if (current == BAD)
|
||||
{
|
||||
recovering = false;
|
||||
resume = DateTime.Now.AddSeconds(random.Next(0, THREE_MINUTES));
|
||||
|
||||
if (!timer.Enabled)
|
||||
{
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
logger.Warn($"Activating local caching and suspending transmission due to bad service health (resume: {resume:HH:mm:ss}).");
|
||||
}
|
||||
else if (previous == BAD)
|
||||
{
|
||||
recovering = true;
|
||||
resume = DateTime.Now < resume ? resume : DateTime.Now.AddSeconds(random.Next(0, THREE_MINUTES));
|
||||
|
||||
logger.Info($"Starting recovery while maintaining local caching (resume: {resume:HH:mm:ss}).");
|
||||
}
|
||||
}
|
||||
|
||||
return current;
|
||||
|
|
|
@ -3,6 +3,5 @@
|
|||
<package id="KGySoft.CoreLibraries" version="8.1.0" targetFramework="net48" />
|
||||
<package id="KGySoft.Drawing" version="8.1.0" targetFramework="net48" />
|
||||
<package id="KGySoft.Drawing.Core" version="8.1.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Web.WebView2" version="1.0.2365.46" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
||||
</packages>
|
|
@ -42,21 +42,22 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Perform_MustShowDisclaimerWhenProctoringEnabled()
|
||||
{
|
||||
var disclaimerShown = false;
|
||||
var count = 0;
|
||||
|
||||
settings.Proctoring.ScreenProctoring.Enabled = true;
|
||||
|
||||
settings.Proctoring.Enabled = true;
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
{
|
||||
disclaimerShown = true;
|
||||
count++;
|
||||
m.Result = MessageBoxResult.Ok;
|
||||
}
|
||||
};
|
||||
|
||||
var result = sut.Perform();
|
||||
|
||||
Assert.IsTrue(disclaimerShown);
|
||||
Assert.AreEqual(1, count);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
|
@ -65,7 +66,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
{
|
||||
var disclaimerShown = false;
|
||||
|
||||
settings.Proctoring.Enabled = true;
|
||||
settings.Proctoring.ScreenProctoring.Enabled = true;
|
||||
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
|
@ -75,7 +77,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
}
|
||||
};
|
||||
|
||||
var result = sut.Perform();
|
||||
var result = sut.Repeat();
|
||||
|
||||
Assert.IsTrue(disclaimerShown);
|
||||
Assert.AreEqual(OperationResult.Aborted, result);
|
||||
|
@ -86,7 +88,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
{
|
||||
var disclaimerShown = false;
|
||||
|
||||
settings.Proctoring.Enabled = false;
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
|
@ -105,21 +106,22 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
[TestMethod]
|
||||
public void Repeat_MustShowDisclaimerWhenProctoringEnabled()
|
||||
{
|
||||
var disclaimerShown = false;
|
||||
var count = 0;
|
||||
|
||||
settings.Proctoring.ScreenProctoring.Enabled = true;
|
||||
|
||||
settings.Proctoring.Enabled = true;
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
{
|
||||
disclaimerShown = true;
|
||||
count++;
|
||||
m.Result = MessageBoxResult.Ok;
|
||||
}
|
||||
};
|
||||
|
||||
var result = sut.Repeat();
|
||||
var result = sut.Perform();
|
||||
|
||||
Assert.IsTrue(disclaimerShown);
|
||||
Assert.AreEqual(1, count);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
|
@ -128,7 +130,8 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
{
|
||||
var disclaimerShown = false;
|
||||
|
||||
settings.Proctoring.Enabled = true;
|
||||
settings.Proctoring.ScreenProctoring.Enabled = true;
|
||||
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
|
@ -149,7 +152,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
{
|
||||
var disclaimerShown = false;
|
||||
|
||||
settings.Proctoring.Enabled = false;
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
|
@ -170,7 +172,6 @@ namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
|||
{
|
||||
var disclaimerShown = false;
|
||||
|
||||
settings.Proctoring.Enabled = true;
|
||||
sut.ActionRequired += (args) =>
|
||||
{
|
||||
if (args is MessageEventArgs m)
|
||||
|
|
|
@ -1,100 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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 Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Runtime.Operations;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||
{
|
||||
[TestClass]
|
||||
public class ProctoringWorkaroundOperationTests
|
||||
{
|
||||
private SessionContext context;
|
||||
private Mock<ILogger> logger;
|
||||
private AppSettings settings;
|
||||
private ProctoringWorkaroundOperation sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
context = new SessionContext();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new AppSettings();
|
||||
|
||||
context.Next = new SessionConfiguration();
|
||||
context.Next.Settings = settings;
|
||||
sut = new ProctoringWorkaroundOperation(logger.Object, context);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Perform_MustSwitchToDisableExplorerShellIfProctoringActive()
|
||||
{
|
||||
settings.Proctoring.Enabled = true;
|
||||
settings.Security.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
||||
var result = sut.Perform();
|
||||
|
||||
Assert.AreEqual(KioskMode.DisableExplorerShell, settings.Security.KioskMode);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Perform_MustDoNothingIfProctoringNotActive()
|
||||
{
|
||||
settings.Proctoring.Enabled = false;
|
||||
settings.Security.KioskMode = KioskMode.None;
|
||||
|
||||
var result = sut.Perform();
|
||||
|
||||
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Repeat_MustSwitchToDisableExplorerShellIfProctoringActive()
|
||||
{
|
||||
settings.Proctoring.Enabled = true;
|
||||
settings.Security.KioskMode = KioskMode.CreateNewDesktop;
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
Assert.AreEqual(KioskMode.DisableExplorerShell, settings.Security.KioskMode);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Repeat_MustDoNothingIfProctoringNotActive()
|
||||
{
|
||||
settings.Proctoring.Enabled = false;
|
||||
settings.Security.KioskMode = KioskMode.None;
|
||||
|
||||
var result = sut.Repeat();
|
||||
|
||||
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Revert_MustDoNothing()
|
||||
{
|
||||
settings.Proctoring.Enabled = true;
|
||||
settings.Security.KioskMode = KioskMode.None;
|
||||
|
||||
var result = sut.Revert();
|
||||
|
||||
Assert.AreEqual(KioskMode.None, settings.Security.KioskMode);
|
||||
Assert.AreEqual(OperationResult.Success, result);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,10 +11,10 @@ using Moq;
|
|||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
using SafeExamBrowser.Runtime.Operations;
|
||||
using SafeExamBrowser.Runtime.Operations.Events;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||
{
|
||||
|
|
|
@ -11,10 +11,10 @@ using Moq;
|
|||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
using SafeExamBrowser.Runtime.Operations;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.UnitTests.Operations
|
||||
{
|
||||
|
|
|
@ -145,7 +145,6 @@
|
|||
<Compile Include="Operations\DisclaimerOperationTests.cs" />
|
||||
<Compile Include="Operations\DisplayMonitorOperationTests.cs" />
|
||||
<Compile Include="Operations\KioskModeOperationTests.cs" />
|
||||
<Compile Include="Operations\ProctoringWorkaroundOperationTests.cs" />
|
||||
<Compile Include="Operations\RemoteSessionOperationTests.cs" />
|
||||
<Compile Include="Operations\ServerOperationTests.cs" />
|
||||
<Compile Include="Operations\ServiceOperationTests.cs" />
|
||||
|
|
|
@ -25,7 +25,9 @@ using SafeExamBrowser.I18n;
|
|||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring;
|
||||
using SafeExamBrowser.Monitoring.Display;
|
||||
using SafeExamBrowser.Monitoring.System;
|
||||
using SafeExamBrowser.Runtime.Communication;
|
||||
using SafeExamBrowser.Runtime.Operations;
|
||||
using SafeExamBrowser.Server;
|
||||
|
@ -82,6 +84,7 @@ namespace SafeExamBrowser.Runtime
|
|||
var remoteSessionDetector = new RemoteSessionDetector(ModuleLogger(nameof(RemoteSessionDetector)));
|
||||
var runtimeHost = new RuntimeHost(appConfig.RuntimeAddress, new HostObjectFactory(), ModuleLogger(nameof(RuntimeHost)), FIVE_SECONDS);
|
||||
var runtimeWindow = uiFactory.CreateRuntimeWindow(appConfig);
|
||||
var sentinel = new SystemSentinel(ModuleLogger(nameof(SystemSentinel)), nativeMethods, registry);
|
||||
var server = new ServerProxy(appConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo);
|
||||
var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)), Interlocutor.Runtime);
|
||||
var sessionContext = new SessionContext();
|
||||
|
@ -101,12 +104,11 @@ namespace SafeExamBrowser.Runtime
|
|||
sessionOperations.Enqueue(new VersionRestrictionOperation(logger, sessionContext, text));
|
||||
sessionOperations.Enqueue(new DisclaimerOperation(logger, sessionContext));
|
||||
sessionOperations.Enqueue(new RemoteSessionOperation(remoteSessionDetector, logger, sessionContext));
|
||||
sessionOperations.Enqueue(new SessionIntegrityOperation(logger, registry, sessionContext));
|
||||
sessionOperations.Enqueue(new SessionIntegrityOperation(logger, sentinel, sessionContext));
|
||||
sessionOperations.Enqueue(new VirtualMachineOperation(vmDetector, 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));
|
||||
sessionOperations.Enqueue(new KioskModeOperation(desktopFactory, desktopMonitor, explorerShell, logger, processFactory, sessionContext));
|
||||
sessionOperations.Enqueue(new ClientOperation(logger, processFactory, proxyFactory, runtimeHost, sessionContext, THIRTY_SECONDS));
|
||||
sessionOperations.Enqueue(new SessionActivationOperation(logger, sessionContext));
|
||||
|
|
|
@ -103,7 +103,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
var logFilePath = $"{'"' + Convert.ToBase64String(Encoding.UTF8.GetBytes(Context.Next.AppConfig.ClientLogFilePath)) + '"'}";
|
||||
var logLevel = Context.Next.Settings.LogLevel.ToString();
|
||||
var runtimeHostUri = Context.Next.AppConfig.RuntimeAddress;
|
||||
var uiMode = Context.Next.Settings.UserInterfaceMode.ToString();
|
||||
var uiMode = Context.Next.Settings.UserInterface.Mode.ToString();
|
||||
|
||||
var clientReady = false;
|
||||
var clientReadyEvent = new AutoResetEvent(false);
|
||||
|
|
|
@ -32,13 +32,13 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
protected abstract void InvokeActionRequired(ActionRequiredEventArgs args);
|
||||
|
||||
protected LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out AppSettings settings, string currentPassword = default(string))
|
||||
protected LoadStatus? TryLoadSettings(Uri uri, UriSource source, out PasswordParameters passwordParams, out AppSettings settings, string currentPassword = default)
|
||||
{
|
||||
passwordParams = new PasswordParameters { Password = string.Empty, IsHash = true };
|
||||
|
||||
var status = configuration.TryLoadSettings(uri, out settings, passwordParams);
|
||||
|
||||
if (status == LoadStatus.PasswordNeeded && currentPassword != default(string))
|
||||
if (status == LoadStatus.PasswordNeeded && currentPassword != default)
|
||||
{
|
||||
passwordParams.Password = currentPassword;
|
||||
passwordParams.IsHash = true;
|
||||
|
@ -46,7 +46,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
status = configuration.TryLoadSettings(uri, out settings, passwordParams);
|
||||
}
|
||||
|
||||
for (int attempts = 0; attempts < 5 && status == LoadStatus.PasswordNeeded; attempts++)
|
||||
for (var attempts = 0; attempts < 5 && status == LoadStatus.PasswordNeeded; attempts++)
|
||||
{
|
||||
var isLocalConfig = source == UriSource.AppData || source == UriSource.ProgramData;
|
||||
var purpose = isLocalConfig ? PasswordRequestPurpose.LocalSettings : PasswordRequestPurpose.Settings;
|
||||
|
|
|
@ -164,13 +164,18 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
result = OperationResult.Aborted;
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success && Context.Current.IsBrowserResource)
|
||||
{
|
||||
HandleReconfigurationByBrowserResource();
|
||||
}
|
||||
|
||||
fileSystem.Delete(uri.LocalPath);
|
||||
logger.Info($"Deleted temporary configuration file '{uri}'.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private OperationResult DetermineLoadResult(Uri uri, UriSource source, AppSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default(string))
|
||||
private OperationResult DetermineLoadResult(Uri uri, UriSource source, AppSettings settings, LoadStatus status, PasswordParameters passwordParams, string currentPassword = default)
|
||||
{
|
||||
var result = OperationResult.Failed;
|
||||
|
||||
|
@ -205,6 +210,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
private OperationResult HandleBrowserResource(Uri uri)
|
||||
{
|
||||
Context.Next.IsBrowserResource = true;
|
||||
Context.Next.Settings.Applications.Blacklist.Clear();
|
||||
Context.Next.Settings.Applications.Whitelist.Clear();
|
||||
Context.Next.Settings.Display.AllowedDisplays = 10;
|
||||
|
@ -222,7 +228,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private OperationResult HandleClientConfiguration(Uri uri, PasswordParameters passwordParams, string currentPassword = default(string))
|
||||
private OperationResult HandleClientConfiguration(Uri uri, PasswordParameters passwordParams, string currentPassword = default)
|
||||
{
|
||||
var isFirstSession = Context.Current == null;
|
||||
var success = TryConfigureClient(uri, passwordParams, currentPassword);
|
||||
|
@ -240,6 +246,12 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return result;
|
||||
}
|
||||
|
||||
private void HandleReconfigurationByBrowserResource()
|
||||
{
|
||||
Context.Next.Settings.Browser.DeleteCookiesOnStartup = false;
|
||||
logger.Info("Some browser settings were overridden in order to retain a potential LMS / web application session.");
|
||||
}
|
||||
|
||||
private void HandleStartUrlQuery(Uri uri, UriSource source)
|
||||
{
|
||||
if (source == UriSource.Reconfiguration && Uri.TryCreate(Context.ReconfigurationUrl, UriKind.Absolute, out var reconfigurationUri))
|
||||
|
@ -247,13 +259,13 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
uri = reconfigurationUri;
|
||||
}
|
||||
|
||||
if (uri != default(Uri) && uri.Query.LastIndexOf('?') > 0)
|
||||
if (uri != default && uri.Query.LastIndexOf('?') > 0)
|
||||
{
|
||||
Context.Next.Settings.Browser.StartUrlQuery = uri.Query.Substring(uri.Query.LastIndexOf('?'));
|
||||
}
|
||||
}
|
||||
|
||||
private bool? TryConfigureClient(Uri uri, PasswordParameters passwordParams, string currentPassword = default(string))
|
||||
private bool? TryConfigureClient(Uri uri, PasswordParameters passwordParams, string currentPassword = default)
|
||||
{
|
||||
var mustAuthenticate = IsRequiredToAuthenticateForClientConfiguration(passwordParams, currentPassword);
|
||||
|
||||
|
@ -304,9 +316,9 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return success;
|
||||
}
|
||||
|
||||
private bool IsRequiredToAuthenticateForClientConfiguration(PasswordParameters passwordParams, string currentPassword = default(string))
|
||||
private bool IsRequiredToAuthenticateForClientConfiguration(PasswordParameters passwordParams, string currentPassword = default)
|
||||
{
|
||||
var mustAuthenticate = currentPassword != default(string);
|
||||
var mustAuthenticate = currentPassword != default;
|
||||
|
||||
if (mustAuthenticate)
|
||||
{
|
||||
|
@ -334,7 +346,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
var authenticated = false;
|
||||
|
||||
for (int attempts = 0; attempts < 5 && !authenticated; attempts++)
|
||||
for (var attempts = 0; attempts < 5 && !authenticated; attempts++)
|
||||
{
|
||||
var success = TryGetPassword(PasswordRequestPurpose.LocalAdministrator, out var password);
|
||||
|
||||
|
@ -384,8 +396,8 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
var isValidUri = false;
|
||||
|
||||
uri = null;
|
||||
source = default(UriSource);
|
||||
uri = default;
|
||||
source = default;
|
||||
|
||||
if (commandLineArgs?.Length > 1)
|
||||
{
|
||||
|
|
|
@ -31,21 +31,11 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
var result = OperationResult.Success;
|
||||
|
||||
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled)
|
||||
{
|
||||
result = ShowVideoProctoringDisclaimer();
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
|
||||
if (Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
|
||||
{
|
||||
result = ShowScreenProctoringDisclaimer();
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.Zoom.Enabled)
|
||||
{
|
||||
result = ShowZoomError();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -53,21 +43,11 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
{
|
||||
var result = OperationResult.Success;
|
||||
|
||||
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled)
|
||||
{
|
||||
result = ShowVideoProctoringDisclaimer();
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
|
||||
if (Context.Next.Settings.Proctoring.ScreenProctoring.Enabled)
|
||||
{
|
||||
result = ShowScreenProctoringDisclaimer();
|
||||
}
|
||||
|
||||
if (result == OperationResult.Success && Context.Next.Settings.Proctoring.Zoom.Enabled)
|
||||
{
|
||||
result = ShowZoomError();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -102,50 +82,5 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Aborted;
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult ShowVideoProctoringDisclaimer()
|
||||
{
|
||||
var args = new MessageEventArgs
|
||||
{
|
||||
Action = MessageBoxAction.OkCancel,
|
||||
Icon = MessageBoxIcon.Information,
|
||||
Message = TextKey.MessageBox_VideoProctoringDisclaimer,
|
||||
Title = TextKey.MessageBox_VideoProctoringDisclaimerTitle
|
||||
};
|
||||
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_WaitDisclaimerConfirmation);
|
||||
ActionRequired?.Invoke(args);
|
||||
|
||||
if (args.Result == MessageBoxResult.Ok)
|
||||
{
|
||||
logger.Info("The user confirmed the video proctoring disclaimer.");
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("The user did not confirm the video proctoring disclaimer! Aborting session initialization...");
|
||||
|
||||
return OperationResult.Aborted;
|
||||
}
|
||||
}
|
||||
|
||||
private OperationResult ShowZoomError()
|
||||
{
|
||||
var args = new MessageEventArgs
|
||||
{
|
||||
Action = MessageBoxAction.Ok,
|
||||
Icon = MessageBoxIcon.Error,
|
||||
Message = TextKey.MessageBox_ZoomNotSupported,
|
||||
Title = TextKey.MessageBox_ZoomNotSupportedTitle
|
||||
};
|
||||
|
||||
logger.Error("Zoom proctoring is enabled but not supported! Aborting session initialization...");
|
||||
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_WaitErrorConfirmation);
|
||||
ActionRequired?.Invoke(args);
|
||||
|
||||
return OperationResult.Aborted;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,49 +0,0 @@
|
|||
/*
|
||||
* Copyright (c) 2024 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.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class ProctoringWorkaroundOperation : SessionOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged { add { } remove { } }
|
||||
|
||||
public ProctoringWorkaroundOperation(ILogger logger, SessionContext context) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
if (Context.Next.Settings.Proctoring.JitsiMeet.Enabled && Context.Next.Settings.Security.KioskMode == KioskMode.CreateNewDesktop)
|
||||
{
|
||||
Context.Next.Settings.Security.KioskMode = KioskMode.DisableExplorerShell;
|
||||
logger.Info("Switched kiosk mode to Disable Explorer Shell due to remote proctoring being enabled.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Repeat()
|
||||
{
|
||||
return Perform();
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -10,8 +10,8 @@ using SafeExamBrowser.Core.Contracts.OperationModel;
|
|||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
using SafeExamBrowser.Runtime.Operations.Events;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
|
|
|
@ -169,6 +169,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
var browserSettings = Context.Next.Settings.Browser;
|
||||
var serverSettings = Context.Next.Settings.Server;
|
||||
|
||||
Context.Next.AppConfig.ServerApi = info.Api;
|
||||
|
@ -178,6 +179,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
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.SessionMode = SessionMode.Server;
|
||||
|
||||
|
|
|
@ -6,31 +6,26 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Registry;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class SessionIntegrityOperation : SessionOperation
|
||||
{
|
||||
private static readonly string USER_PATH = $@"{Environment.ExpandEnvironmentVariables("%LocalAppData%")}\Microsoft\Windows\Cursors\";
|
||||
private static readonly string SYSTEM_PATH = $@"{Environment.ExpandEnvironmentVariables("%SystemRoot%")}\Cursors\";
|
||||
|
||||
private readonly ILogger logger;
|
||||
private readonly IRegistry registry;
|
||||
private readonly ISystemSentinel sentinel;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public SessionIntegrityOperation(ILogger logger, IRegistry registry, SessionContext context) : base(context)
|
||||
public SessionIntegrityOperation(ILogger logger, ISystemSentinel sentinel, SessionContext context) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.registry = registry;
|
||||
this.sentinel = sentinel;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
|
@ -39,8 +34,11 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_VerifySessionIntegrity);
|
||||
|
||||
success &= VerifyCursorConfiguration();
|
||||
success &= VerifyEaseOfAccessConfiguration();
|
||||
success &= InitializeStickyKeys();
|
||||
success &= VerifyCursors();
|
||||
success &= VerifyEaseOfAccess();
|
||||
|
||||
LogResult(success);
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
@ -51,43 +49,60 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_VerifySessionIntegrity);
|
||||
|
||||
success &= VerifyCursorConfiguration();
|
||||
success &= VerifyEaseOfAccessConfiguration();
|
||||
success &= InitializeStickyKeys();
|
||||
success &= VerifyCursors();
|
||||
success &= VerifyEaseOfAccess();
|
||||
|
||||
LogResult(success);
|
||||
|
||||
return success ? OperationResult.Success : OperationResult.Failed;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
FinalizeStickyKeys();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private bool VerifyCursorConfiguration()
|
||||
private void FinalizeStickyKeys()
|
||||
{
|
||||
sentinel.RevertStickyKeys();
|
||||
}
|
||||
|
||||
private bool InitializeStickyKeys()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
sentinel.RevertStickyKeys();
|
||||
|
||||
if (!Context.Next.Settings.Security.AllowStickyKeys)
|
||||
{
|
||||
success = sentinel.DisableStickyKeys();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private void LogResult(bool success)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Successfully ensured session integrity.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Error("Failed to ensure session integrity! Aborting session initialization...");
|
||||
}
|
||||
}
|
||||
|
||||
private bool VerifyCursors()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
if (Context.Next.Settings.Security.VerifyCursorConfiguration)
|
||||
{
|
||||
logger.Info($"Attempting to verify cursor configuration...");
|
||||
|
||||
success = registry.TryGetNames(RegistryValue.UserHive.Cursors_Key, out var cursors);
|
||||
|
||||
if (success)
|
||||
{
|
||||
foreach (var cursor in cursors.Where(c => !string.IsNullOrWhiteSpace(c)))
|
||||
{
|
||||
success &= VerifyCursor(cursor);
|
||||
}
|
||||
}
|
||||
|
||||
if (success)
|
||||
{
|
||||
logger.Info("Cursor configuration successfully verified.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to verify cursor configuration or configuration is compromised! Aborting session initialization...");
|
||||
}
|
||||
success = sentinel.VerifyCursors();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -97,65 +112,22 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return success;
|
||||
}
|
||||
|
||||
private bool VerifyCursor(string cursor)
|
||||
private bool VerifyEaseOfAccess()
|
||||
{
|
||||
var success = true;
|
||||
|
||||
success &= registry.TryRead(RegistryValue.UserHive.Cursors_Key, cursor, out var value);
|
||||
success &= !(value is string) || (value is string path && (string.IsNullOrWhiteSpace(path) || IsValidCursorPath(path)));
|
||||
var success = sentinel.VerifyEaseOfAccess();
|
||||
|
||||
if (!success)
|
||||
{
|
||||
if (value != default)
|
||||
{
|
||||
logger.Warn($"Configuration of cursor '{cursor}' is compromised: '{value}'!");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Failed to verify configuration of cursor '{cursor}'!");
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool IsValidCursorPath(string path)
|
||||
{
|
||||
return path.StartsWith(USER_PATH, StringComparison.OrdinalIgnoreCase) || path.StartsWith(SYSTEM_PATH, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
private bool VerifyEaseOfAccessConfiguration()
|
||||
{
|
||||
var success = false;
|
||||
|
||||
logger.Info($"Attempting to verify ease of access configuration...");
|
||||
|
||||
if (registry.TryRead(RegistryValue.MachineHive.EaseOfAccess_Key, RegistryValue.MachineHive.EaseOfAccess_Name, out var value))
|
||||
{
|
||||
if (value is string s && string.IsNullOrWhiteSpace(s))
|
||||
if (Context.Current?.Settings.Service.IgnoreService == false)
|
||||
{
|
||||
logger.Info($"Ease of access configuration is compromised but service was active in the current session.");
|
||||
success = true;
|
||||
logger.Info("Ease of access configuration successfully verified.");
|
||||
}
|
||||
else if (!Context.Next.Settings.Service.IgnoreService)
|
||||
{
|
||||
logger.Info($"Ease of access configuration is compromised but service will be active in the next session.");
|
||||
success = true;
|
||||
logger.Info($"Ease of access configuration is compromised ('{value}'), but service will be active in the next session.");
|
||||
}
|
||||
else if (Context.Current?.Settings.Service.IgnoreService == false)
|
||||
{
|
||||
success = true;
|
||||
logger.Info($"Ease of access configuration is set ('{value}'), but service was active in the current session.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn($"Ease of access configuration is compromised: '{value}'! Aborting session initialization...");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
success = true;
|
||||
logger.Info("Ease of access configuration successfully verified (value does not exist).");
|
||||
}
|
||||
|
||||
return success;
|
||||
|
|
|
@ -10,17 +10,17 @@ using SafeExamBrowser.Core.Contracts.OperationModel;
|
|||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts;
|
||||
using SafeExamBrowser.Runtime.Operations.Events;
|
||||
using SafeExamBrowser.Settings.Security;
|
||||
using SafeExamBrowser.SystemComponents.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations
|
||||
{
|
||||
internal class VirtualMachineOperation : SessionOperation
|
||||
{
|
||||
private IVirtualMachineDetector detector;
|
||||
private ILogger logger;
|
||||
private readonly IVirtualMachineDetector detector;
|
||||
private readonly ILogger logger;
|
||||
|
||||
public override event ActionRequiredEventHandler ActionRequired;
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue