Compare commits
24 Commits
Author | SHA1 | Date | |
---|---|---|---|
213ab539cf | |||
9a9b753049 | |||
5148abede7 | |||
dd82d45ed8 | |||
4827ae1afc | |||
f79146d6fb | |||
59747140c7 | |||
ae5dc61021 | |||
626b351dff | |||
1a350b6c13 | |||
![]() |
9ed5135f69 | ||
25495595ba | |||
8a09160d31 | |||
354f731a6f | |||
9b2367a4a3 | |||
4948d5946f | |||
84b26a2295 | |||
811aea6bd2 | |||
b71e8bd82f | |||
a6b60d72bd | |||
2d36fecb45 | |||
058d48196a | |||
332c6460c7 | |||
fc6a2f7509 |
24
.github/ISSUE_TEMPLATE/bug-report.md
vendored
24
.github/ISSUE_TEMPLATE/bug-report.md
vendored
@@ -1,18 +1,17 @@
|
||||
---
|
||||
name: Bug Report
|
||||
about: Create a bug report to help us improve the Safe Exam Browser Patch
|
||||
about: Create a bug report to help us improve Safe Exam Browser.
|
||||
title: ''
|
||||
labels: bug
|
||||
assignees: usefulstuffs
|
||||
labels: ''
|
||||
assignees: dbuechel
|
||||
|
||||
---
|
||||
|
||||
> [!IMPORTANT]
|
||||
> - Please _always_ consult the FAQs before creating an issue: https://github.com/school-cheating/SEBPatch/wiki/FAQs.
|
||||
> - Please _always_ consult the documentation first before creating a bug 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`.
|
||||
> - Please follow this issue template. Saves me some work while reading all issues.
|
||||
|
||||
**Bug Description**
|
||||
**Describe the Bug**
|
||||
A clear and concise description of what the bug is.
|
||||
|
||||
**Steps to Reproduce**
|
||||
@@ -30,18 +29,7 @@ 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]
|
||||
- SEB patch version: [e.g. 1.5.1]
|
||||
- SEB-Version [e.g. SEB 3.0.1]
|
||||
|
||||
**Additional Context**
|
||||
Add any other context about the problem here.
|
||||
|
||||
**SEB Logs**
|
||||
```
|
||||
Paste here the SEB logs
|
||||
```
|
||||
|
||||
**SEB Patcher logs (optional, read below)**
|
||||
```
|
||||
Paste here the SEB patcher logs (if issue is SEB patcher related, else just ignore this section)
|
||||
```
|
||||
|
11
.github/ISSUE_TEMPLATE/config.yml
vendored
11
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,11 +0,0 @@
|
||||
blank_issues_enabled: false
|
||||
contact_links:
|
||||
- name: FAQs
|
||||
url: https://github.com/school-cheating/SEBPatch/wiki/FAQs
|
||||
about: Before opening an issue, check out the FAQs. Any issue corresponding to the FAQs will be ignored and closed.
|
||||
- name: Questions
|
||||
url: https://github.com/school-cheating/SEBPatch/discussions/categories/q-a
|
||||
about: Ask questions here about the Safe Exam Browser Patch
|
||||
- name: Feature Requests
|
||||
url: https://github.com/school-cheating/SEBPatch/discussions/categories/ideas
|
||||
about: Propose a feature or a change for the Safe Exam Browser Patch.
|
20
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
20
.github/ISSUE_TEMPLATE/feature-request.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
name: Feature Request
|
||||
about: Suggest an idea or new feature for Safe Exam Browser.
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: dbuechel
|
||||
|
||||
---
|
||||
|
||||
**Is your feature request related to a problem? Please describe.**
|
||||
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
||||
|
||||
**Describe the solution you'd like**
|
||||
A clear and concise description of what you want to happen.
|
||||
|
||||
**Describe alternatives you've considered**
|
||||
A clear and concise description of any alternative solutions or features you've considered.
|
||||
|
||||
**Additional context**
|
||||
Add any other context or screenshots about the feature request here.
|
54
.github/workflows/codeql.yml
vendored
Normal file
54
.github/workflows/codeql.yml
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
name: "CodeQL"
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "*" ]
|
||||
pull_request:
|
||||
branches: [ "master", "*" ]
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: "windows-latest"
|
||||
timeout-minutes: 360
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'csharp', 'javascript-typescript' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
queries: security-extended,security-and-quality
|
||||
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun
|
||||
|
||||
# If the Autobuild fails above, remove it and uncomment the following three lines.
|
||||
# modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance.
|
||||
|
||||
# - run: |
|
||||
# echo "Run, Build Application using script"
|
||||
# ./location_of_script_within_repo/buildscript.sh
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
with:
|
||||
category: "/language:${{matrix.language}}"
|
@@ -1,10 +1,12 @@
|
||||
# Safe Exam Browser Patch
|
||||
[](https://ci.vichingo455.freeddns.org/project/Vichingo455/sebpatch)
|
||||
|
||||
A patch to bypass Safe Exam Browser restrictions.
|
||||
|
||||
- Currently supported SEB version: 3.9.0.787
|
||||
- Currently supported SEB version: 3.10.0.826
|
||||
|
||||
## [HOW TO INSTALL](https://github.com/school-cheating/SEBPatch/wiki/Instructions)
|
||||
I put this bigger so you actually read it and stop asking how to install the patch.
|
||||
## Downloading and installing
|
||||
You can get the latest executable from [releases](https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/releases/latest). If you need help about the installation, check the [wiki](https://git.vichingo455.freeddns.org/school-cheating/SEBPatch/wiki).
|
||||
|
||||
## Mirrors
|
||||
In case you can't download from the latest release, here is a list of mirrors (will be updated eventually):
|
||||
|
@@ -18,6 +18,11 @@ namespace SafeExamBrowser.Applications.Contracts
|
||||
/// </summary>
|
||||
Error,
|
||||
|
||||
/// <summary>
|
||||
/// The application has been found but is invalid (e.g. because it is not the correct version or has been manipulated).
|
||||
/// </summary>
|
||||
Invalid,
|
||||
|
||||
/// <summary>
|
||||
/// The application could not be found on the system.
|
||||
/// </summary>
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -63,78 +63,82 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
<HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath>
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
@@ -184,11 +188,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -196,13 +197,17 @@
|
||||
<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.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
|
||||
</Project>
|
@@ -4,7 +4,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
@@ -12,31 +12,31 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -1,24 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" />
|
||||
<package id="Castle.Core" version="5.2.1" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Moq" version="4.20.72" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
|
||||
</packages>
|
@@ -8,7 +8,9 @@
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
@@ -43,42 +45,62 @@ namespace SafeExamBrowser.Applications
|
||||
public FactoryResult TryCreate(WhitelistApplication settings, out IApplication<IApplicationWindow> application)
|
||||
{
|
||||
var name = $"'{settings.DisplayName}' ({settings.ExecutableName})";
|
||||
var result = FactoryResult.Error;
|
||||
|
||||
application = default;
|
||||
|
||||
try
|
||||
{
|
||||
var success = TryFindApplication(settings, out var executablePath);
|
||||
var found = TryFindApplication(settings, out var executablePath);
|
||||
var valid = found && VerifyApplication(executablePath, name, settings);
|
||||
|
||||
if (success)
|
||||
if (found && valid)
|
||||
{
|
||||
application = BuildApplication(executablePath, settings);
|
||||
application.Initialize();
|
||||
|
||||
logger.Debug($"Successfully initialized application {name}.");
|
||||
|
||||
return FactoryResult.Success;
|
||||
application = InitializeApplication(executablePath, settings);
|
||||
}
|
||||
|
||||
logger.Error($"Could not find application {name}!");
|
||||
|
||||
return FactoryResult.NotFound;
|
||||
result = DetermineResult(name, found, valid);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Unexpected error while trying to initialize application {name}!", e);
|
||||
}
|
||||
|
||||
return FactoryResult.Error;
|
||||
return result;
|
||||
}
|
||||
|
||||
private IApplication<IApplicationWindow> BuildApplication(string executablePath, WhitelistApplication settings)
|
||||
private FactoryResult DetermineResult(string name, bool found, bool valid)
|
||||
{
|
||||
var result = default(FactoryResult);
|
||||
|
||||
if (!found)
|
||||
{
|
||||
result = FactoryResult.NotFound;
|
||||
logger.Error($"Could not find application {name}!");
|
||||
}
|
||||
else if (!valid)
|
||||
{
|
||||
result = FactoryResult.Invalid;
|
||||
logger.Error($"The application {name} is not valid or has been manipulated!");
|
||||
}
|
||||
else
|
||||
{
|
||||
result = FactoryResult.Success;
|
||||
logger.Debug($"Successfully initialized application {name}.");
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private IApplication<IApplicationWindow> InitializeApplication(string executablePath, WhitelistApplication settings)
|
||||
{
|
||||
const int ONE_SECOND = 1000;
|
||||
|
||||
var applicationLogger = logger.CloneFor(settings.DisplayName);
|
||||
var application = new ExternalApplication(applicationMonitor, executablePath, applicationLogger, nativeMethods, processFactory, settings, ONE_SECOND);
|
||||
|
||||
application.Initialize();
|
||||
|
||||
return application;
|
||||
}
|
||||
|
||||
@@ -143,5 +165,99 @@ namespace SafeExamBrowser.Applications
|
||||
|
||||
return default;
|
||||
}
|
||||
|
||||
private bool VerifyApplication(string executablePath, string name, WhitelistApplication settings)
|
||||
{
|
||||
var valid = true;
|
||||
|
||||
valid &= VerifyName(executablePath, name, settings);
|
||||
valid &= VerifyOriginalName(executablePath, name, settings);
|
||||
valid &= VerifySignature(executablePath, name, settings);
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private bool VerifyName(string executablePath, string name, WhitelistApplication settings)
|
||||
{
|
||||
var valid = Path.GetFileName(executablePath).Equals(settings.ExecutableName, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
logger.Warn($"The executable name of application {name} at '{executablePath}' does not match the configured value!");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private bool VerifyOriginalName(string executablePath, string name, WhitelistApplication settings)
|
||||
{
|
||||
var ignoreOriginalName = string.IsNullOrWhiteSpace(settings.OriginalName);
|
||||
var valid = ignoreOriginalName;
|
||||
|
||||
if (!ignoreOriginalName && TryLoadOriginalName(executablePath, out var originalName))
|
||||
{
|
||||
valid = originalName.Equals(settings.OriginalName, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
logger.Warn($"The original name of application {name} at '{executablePath}' does not match the configured value!");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private bool VerifySignature(string executablePath, string name, WhitelistApplication settings)
|
||||
{
|
||||
var ignoreSignature = string.IsNullOrWhiteSpace(settings.Signature);
|
||||
var valid = ignoreSignature;
|
||||
|
||||
if (!ignoreSignature && TryLoadSignature(executablePath, out var signature))
|
||||
{
|
||||
valid = signature.Equals(settings.Signature, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (!valid)
|
||||
{
|
||||
logger.Warn($"The signature of application {name} at '{executablePath}' does not match the configured value!");
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
private bool TryLoadOriginalName(string path, out string originalName)
|
||||
{
|
||||
originalName = default;
|
||||
|
||||
try
|
||||
{
|
||||
originalName = FileVersionInfo.GetVersionInfo(path).OriginalFilename;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to load original name for '{path}'!", e);
|
||||
}
|
||||
|
||||
return originalName != default;
|
||||
}
|
||||
|
||||
private bool TryLoadSignature(string path, out string signature)
|
||||
{
|
||||
signature = default;
|
||||
|
||||
try
|
||||
{
|
||||
using (var certificate = X509Certificate.CreateFromSignedFile(path))
|
||||
{
|
||||
signature = certificate.GetCertHashString();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to load signature for '{path}'!", e);
|
||||
}
|
||||
|
||||
return signature != default;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -102,13 +102,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedResult()
|
||||
{
|
||||
var rule = new Mock<IRule>();
|
||||
|
||||
rule.SetupGet(r => r.Result).Returns((FilterResult) (-1));
|
||||
sut.Load(rule.Object);
|
||||
|
||||
Assert.ThrowsExactly<NotImplementedException>(() => sut.Load(rule.Object));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -33,10 +33,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(NotImplementedException))]
|
||||
public void MustNotAllowUnsupportedFilterType()
|
||||
{
|
||||
sut.CreateRule((FilterRuleType) (-1));
|
||||
Assert.ThrowsExactly<NotImplementedException>(() => sut.CreateRule((FilterRuleType) (-1)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,17 +51,15 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void MustNotAllowUndefinedExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings());
|
||||
Assert.ThrowsExactly<ArgumentNullException>(() => sut.Initialize(new FilterRuleSettings()));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentException))]
|
||||
public void MustValidateExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" });
|
||||
Assert.ThrowsExactly<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = "ç+\"}%&*/(+)=?{=*+¦]@#°§]`?´^¨'°[¬|¢" }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -51,10 +51,9 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void MustNotAllowUndefinedExpression()
|
||||
{
|
||||
sut.Initialize(new FilterRuleSettings());
|
||||
Assert.ThrowsExactly<ArgumentNullException>(() => sut.Initialize(new FilterRuleSettings()));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -73,7 +72,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Filters.Rules
|
||||
|
||||
foreach (var expression in invalid)
|
||||
{
|
||||
Assert.ThrowsException<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = expression }));
|
||||
Assert.ThrowsExactly<ArgumentException>(() => sut.Initialize(new FilterRuleSettings { Expression = expression }));
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -12,6 +12,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
@@ -41,6 +42,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var integrations = new Integration[]
|
||||
{
|
||||
new GenericIntegration(new Mock<ILogger>().Object),
|
||||
new EdxIntegration(new Mock<ILogger>().Object),
|
||||
new MoodleIntegration(new Mock<ILogger>().Object)
|
||||
};
|
||||
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
@@ -48,7 +56,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
resourceHandler = new ResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, default, settings, windowSettings, text.Object);
|
||||
resourceHandler = new ResourceHandler(appConfig, filter.Object, integrations, keyGenerator.Object, logger.Object, default, settings, windowSettings, text.Object);
|
||||
|
||||
sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings);
|
||||
}
|
||||
@@ -285,7 +293,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
|
||||
private class TestableRequestHandler : RequestHandler
|
||||
{
|
||||
internal TestableRequestHandler(AppConfig appConfig, IRequestFilter filter, ILogger logger, ResourceHandler resourceHandler, BrowserSettings settings, WindowSettings windowSettings) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings)
|
||||
internal TestableRequestHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
ILogger logger,
|
||||
ResourceHandler resourceHandler,
|
||||
BrowserSettings settings,
|
||||
WindowSettings windowSettings) : base(appConfig, filter, logger, resourceHandler, settings, windowSettings)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -7,6 +7,7 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
@@ -14,6 +15,7 @@ using CefSharp;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
@@ -42,6 +44,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var integrations = new Integration[]
|
||||
{
|
||||
new GenericIntegration(new Mock<ILogger>().Object),
|
||||
new EdxIntegration(new Mock<ILogger>().Object),
|
||||
new MoodleIntegration(new Mock<ILogger>().Object)
|
||||
};
|
||||
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
@@ -50,7 +59,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
|
||||
sut = new TestableResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, SessionMode.Server, settings, windowSettings, text.Object);
|
||||
sut = new TestableResourceHandler(appConfig, filter.Object, integrations, keyGenerator.Object, logger.Object, SessionMode.Server, settings, windowSettings, text.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -325,12 +334,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
||||
internal TestableResourceHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
IEnumerable<Integration> integrations,
|
||||
IKeyGenerator keyGenerator,
|
||||
ILogger logger,
|
||||
SessionMode sessionMode,
|
||||
BrowserSettings settings,
|
||||
WindowSettings windowSettings,
|
||||
IText text) : base(appConfig, filter, keyGenerator, logger, sessionMode, settings, windowSettings, text)
|
||||
IText text) : base(appConfig, filter, integrations, keyGenerator, logger, sessionMode, settings, windowSettings, text)
|
||||
{
|
||||
}
|
||||
|
||||
|
@@ -1,12 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -66,84 +66,88 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
<HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll</HintPath>
|
||||
<Reference Include="CefSharp, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.Core, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll</HintPath>
|
||||
<Reference Include="CefSharp.Core, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath>
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Windows.Forms" />
|
||||
<Reference Include="System.Xml" />
|
||||
@@ -200,11 +204,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -212,18 +213,22 @@
|
||||
<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\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
|
||||
</Project>
|
@@ -4,11 +4,11 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
@@ -16,11 +16,11 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="CefSharp" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-129.0.110.0" newVersion="129.0.110.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-138.0.170.0" newVersion="138.0.170.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="CefSharp.Core" publicKeyToken="40c4b6fc221f4138" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-129.0.110.0" newVersion="129.0.110.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-138.0.170.0" newVersion="138.0.170.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
@@ -28,27 +28,27 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -1,27 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="131.3.50" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="131.3.5" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="131.3.5" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" />
|
||||
<package id="Castle.Core" version="5.2.1" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="139.0.28" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Moq" version="4.20.72" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
|
||||
</packages>
|
@@ -18,6 +18,7 @@ using SafeExamBrowser.Applications.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.Core.Contracts.Resources.Icons;
|
||||
@@ -119,6 +120,7 @@ namespace SafeExamBrowser.Browser
|
||||
InitializeCookies();
|
||||
InitializeDownAndUploadDirectory();
|
||||
InitializeIntegrityKeys();
|
||||
InitializePreferences();
|
||||
|
||||
logger.Info("Initialized browser.");
|
||||
}
|
||||
@@ -167,6 +169,12 @@ namespace SafeExamBrowser.Browser
|
||||
private void CreateNewWindow(PopupRequestedEventArgs args = default)
|
||||
{
|
||||
var id = ++windowIdCounter;
|
||||
var integrations = new Integration[]
|
||||
{
|
||||
new GenericIntegration(logger.CloneFor($"{nameof(GenericIntegration)} #{id}")),
|
||||
new EdxIntegration(logger.CloneFor($"{nameof(EdxIntegration)} #{id}")),
|
||||
new MoodleIntegration(logger.CloneFor($"{nameof(MoodleIntegration)} #{id}"))
|
||||
};
|
||||
var isMainWindow = windows.Count == 0;
|
||||
var startUrl = GenerateStartUrl();
|
||||
var windowLogger = logger.CloneFor($"Browser Window #{id}");
|
||||
@@ -176,6 +184,7 @@ namespace SafeExamBrowser.Browser
|
||||
fileSystemDialog,
|
||||
hashAlgorithm,
|
||||
id,
|
||||
integrations,
|
||||
isMainWindow,
|
||||
keyGenerator,
|
||||
windowLogger,
|
||||
@@ -197,7 +206,7 @@ namespace SafeExamBrowser.Browser
|
||||
window.InitializeControl();
|
||||
windows.Add(window);
|
||||
|
||||
if (args != default(PopupRequestedEventArgs))
|
||||
if (args != default)
|
||||
{
|
||||
args.Window = window;
|
||||
}
|
||||
@@ -213,6 +222,7 @@ namespace SafeExamBrowser.Browser
|
||||
private void DeleteCookies()
|
||||
{
|
||||
var callback = new TaskDeleteCookiesCallback();
|
||||
var cookieManager = Cef.GetGlobalCookieManager();
|
||||
|
||||
callback.Task.ContinueWith(task =>
|
||||
{
|
||||
@@ -226,7 +236,7 @@ namespace SafeExamBrowser.Browser
|
||||
}
|
||||
});
|
||||
|
||||
if (Cef.GetGlobalCookieManager().DeleteCookies(callback: callback))
|
||||
if (cookieManager != default && cookieManager.DeleteCookies(callback: callback))
|
||||
{
|
||||
logger.Debug("Successfully initiated cookie deletion.");
|
||||
}
|
||||
@@ -316,7 +326,6 @@ namespace SafeExamBrowser.Browser
|
||||
|
||||
cefSettings.AcceptLanguageList = CultureInfo.CurrentUICulture.Name;
|
||||
cefSettings.CachePath = appConfig.BrowserCachePath;
|
||||
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
|
||||
cefSettings.LogFile = appConfig.BrowserLogFilePath;
|
||||
cefSettings.LogSeverity = error ? LogSeverity.Error : (warning ? LogSeverity.Warning : LogSeverity.Info);
|
||||
cefSettings.PersistSessionCookies = !settings.DeleteCookiesOnStartup || !settings.DeleteCookiesOnShutdown;
|
||||
@@ -339,6 +348,7 @@ namespace SafeExamBrowser.Browser
|
||||
|
||||
cefSettings.CefCommandLineArgs.Add("enable-media-stream");
|
||||
cefSettings.CefCommandLineArgs.Add("enable-usermedia-screen-capturing");
|
||||
cefSettings.CefCommandLineArgs.Add("touch-events", "enabled");
|
||||
cefSettings.CefCommandLineArgs.Add("use-fake-ui-for-media-stream");
|
||||
|
||||
InitializeProxySettings(cefSettings);
|
||||
@@ -417,6 +427,18 @@ namespace SafeExamBrowser.Browser
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializePreferences()
|
||||
{
|
||||
Cef.UIThreadTaskFactory.StartNew(() =>
|
||||
{
|
||||
using (var requestContext = Cef.GetGlobalRequestContext())
|
||||
{
|
||||
requestContext.SetPreference("autofill.credit_card_enabled", false, out _);
|
||||
requestContext.SetPreference("autofill.profile_enabled", false, out _);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void InitializeProxySettings(CefSettings cefSettings)
|
||||
{
|
||||
if (settings.Proxy.Policy == ProxyPolicy.Custom)
|
||||
@@ -506,6 +528,7 @@ namespace SafeExamBrowser.Browser
|
||||
private void Window_ResetRequested()
|
||||
{
|
||||
logger.Info("Attempting to reset browser...");
|
||||
|
||||
AwaitReady();
|
||||
|
||||
foreach (var window in windows)
|
||||
|
@@ -23,6 +23,7 @@ namespace SafeExamBrowser.Browser
|
||||
{
|
||||
private readonly Clipboard clipboard;
|
||||
private readonly ICefSharpControl control;
|
||||
private readonly IContextMenuHandler contextMenuHandler;
|
||||
private readonly IDialogHandler dialogHandler;
|
||||
private readonly IDisplayHandler displayHandler;
|
||||
private readonly IDownloadHandler downloadHandler;
|
||||
@@ -47,6 +48,7 @@ namespace SafeExamBrowser.Browser
|
||||
public BrowserControl(
|
||||
Clipboard clipboard,
|
||||
ICefSharpControl control,
|
||||
IContextMenuHandler contextMenuHandler,
|
||||
IDialogHandler dialogHandler,
|
||||
IDisplayHandler displayHandler,
|
||||
IDownloadHandler downloadHandler,
|
||||
@@ -58,8 +60,9 @@ namespace SafeExamBrowser.Browser
|
||||
IRenderProcessMessageHandler renderProcessMessageHandler,
|
||||
IRequestHandler requestHandler)
|
||||
{
|
||||
this.control = control;
|
||||
this.clipboard = clipboard;
|
||||
this.control = control;
|
||||
this.contextMenuHandler = contextMenuHandler;
|
||||
this.dialogHandler = dialogHandler;
|
||||
this.displayHandler = displayHandler;
|
||||
this.downloadHandler = downloadHandler;
|
||||
@@ -89,31 +92,20 @@ namespace SafeExamBrowser.Browser
|
||||
{
|
||||
control.BrowserCore.EvaluateScriptAsync(code).ContinueWith(t =>
|
||||
{
|
||||
callback?.Invoke(new JavaScriptResult
|
||||
{
|
||||
Message = t.Result.Message,
|
||||
Result = t.Result.Result,
|
||||
Success = t.Result.Success
|
||||
});
|
||||
callback?.Invoke(new JavaScriptResult { Message = t.Result.Message, Result = t.Result.Result, Success = t.Result.Success });
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
Task.Run(() => callback?.Invoke(new JavaScriptResult
|
||||
{
|
||||
Message = "JavaScript can't be executed in main frame!",
|
||||
Success = false
|
||||
}));
|
||||
Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = "Could not execute JavaScript in main frame!", Success = false }));
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'!", e);
|
||||
Task.Run(() => callback?.Invoke(new JavaScriptResult
|
||||
{
|
||||
Message = $"Failed to execute JavaScript '{(code.Length > 50 ? code.Take(50) : code)}'! Reason: {e.Message}",
|
||||
Success = false
|
||||
}));
|
||||
var message = "Failed to execute JavaScript in main frame!";
|
||||
|
||||
logger.Error(message, e);
|
||||
Task.Run(() => callback?.Invoke(new JavaScriptResult { Message = $"{message} Reason: {e.Message}", Success = false }));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,10 +121,13 @@ namespace SafeExamBrowser.Browser
|
||||
control.AddressChanged += (o, e) => AddressChanged?.Invoke(e.Address);
|
||||
control.AuthCredentialsRequired += (w, b, o, i, h, p, r, s, c, a) => a.Value = requestHandler.GetAuthCredentials(w, b, o, i, h, p, r, s, c);
|
||||
control.BeforeBrowse += (w, b, f, r, u, i, a) => a.Value = requestHandler.OnBeforeBrowse(w, b, f, r, u, i);
|
||||
control.BeforeContextMenu += (w, b, f, p, m) => contextMenuHandler.OnBeforeContextMenu(w, b, f, p, m);
|
||||
control.BeforeDownload += (w, b, d, c, a) => a.Value = a.Value = downloadHandler.OnBeforeDownload(w, b, d, c);
|
||||
control.BeforeUnloadDialog += (w, b, m, r, c, a) => a.Value = javaScriptDialogHandler.OnBeforeUnloadDialog(w, b, m, r, c);
|
||||
control.CanDownload += (w, b, u, r, a) => a.Value = downloadHandler.CanDownload(w, b, u, r);
|
||||
control.ContextCreated += (w, b, f) => renderProcessMessageHandler.OnContextCreated(w, b, f);
|
||||
control.ContextMenuCommand += (w, b, f, p, c, e, a) => a.Value = contextMenuHandler.OnContextMenuCommand(w, b, f, p, c, e);
|
||||
control.ContextMenuDismissed += (w, b, f) => contextMenuHandler.OnContextMenuDismissed(w, b, f);
|
||||
control.ContextReleased += (w, b, f) => renderProcessMessageHandler.OnContextReleased(w, b, f);
|
||||
control.DialogClosed += (w, b) => javaScriptDialogHandler.OnDialogClosed(w, b);
|
||||
control.DownloadUpdated += (w, b, d, c) => downloadHandler.OnDownloadUpdated(w, b, d, c);
|
||||
@@ -152,6 +147,7 @@ namespace SafeExamBrowser.Browser
|
||||
control.PreKeyEvent += (IWebBrowser w, IBrowser b, KeyType t, int k, int n, CefEventFlags m, bool i, ref bool s, GenericEventArgs a) => a.Value = keyboardHandler.OnPreKeyEvent(w, b, t, k, n, m, i, ref s);
|
||||
control.ResetDialogState += (w, b) => javaScriptDialogHandler.OnResetDialogState(w, b);
|
||||
control.ResourceRequestHandlerRequired += (IWebBrowser w, IBrowser b, IFrame f, IRequest r, bool n, bool d, string i, ref bool h, ResourceRequestEventArgs a) => a.Handler = requestHandler.GetResourceRequestHandler(w, b, f, r, n, d, i, ref h);
|
||||
control.RunContextMenu += (w, b, f, p, m, c, a) => a.Value = contextMenuHandler.RunContextMenu(w, b, f, p, m, c);
|
||||
control.SetFocus += (w, b, s, a) => a.Value = focusHandler.OnSetFocus(w, b, s);
|
||||
control.TakeFocus += (w, b, n) => focusHandler.OnTakeFocus(w, b, n);
|
||||
control.TitleChanged += (o, e) => TitleChanged?.Invoke(e.Title);
|
||||
@@ -193,9 +189,21 @@ namespace SafeExamBrowser.Browser
|
||||
control.BrowserCore.SetZoomLevel(level);
|
||||
}
|
||||
|
||||
private void Clipboard_Changed(long id)
|
||||
private void Clipboard_Changed(string id)
|
||||
{
|
||||
ExecuteJavaScript($"SafeExamBrowser.clipboard.update({id}, '{clipboard.Content}');");
|
||||
try
|
||||
{
|
||||
var script = $"SafeExamBrowser.clipboard.update('{id}', '{clipboard.Content}');";
|
||||
|
||||
foreach (var frame in control.BrowserCore?.GetAllFrames() ?? Enumerable.Empty<IFrame>())
|
||||
{
|
||||
frame.EvaluateScriptAsync(script);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to update JavaScript clipboard!", e);
|
||||
}
|
||||
}
|
||||
|
||||
private void Control_IsBrowserInitializedChanged(object sender, EventArgs e)
|
||||
@@ -208,7 +216,7 @@ namespace SafeExamBrowser.Browser
|
||||
|
||||
private void WebBrowser_JavascriptMessageReceived(object sender, JavascriptMessageReceivedEventArgs e)
|
||||
{
|
||||
clipboard.Process(e);
|
||||
clipboard.Update(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -19,6 +19,7 @@ using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
using SafeExamBrowser.Browser.Filters;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
using SafeExamBrowser.Browser.Wrapper;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
@@ -52,6 +53,7 @@ namespace SafeExamBrowser.Browser
|
||||
private readonly IFileSystemDialog fileSystemDialog;
|
||||
private readonly IHashAlgorithm hashAlgorithm;
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly IEnumerable<Integration> integrations;
|
||||
private readonly IKeyGenerator keyGenerator;
|
||||
private readonly IModuleLogger logger;
|
||||
private readonly IMessageBox messageBox;
|
||||
@@ -97,6 +99,7 @@ namespace SafeExamBrowser.Browser
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
int id,
|
||||
IEnumerable<Integration> integrations,
|
||||
bool isMainWindow,
|
||||
IKeyGenerator keyGenerator,
|
||||
IModuleLogger logger,
|
||||
@@ -113,6 +116,7 @@ namespace SafeExamBrowser.Browser
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.httpClient = new HttpClient();
|
||||
this.Id = id;
|
||||
this.integrations = integrations;
|
||||
this.IsMainWindow = isMainWindow;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.logger = logger;
|
||||
@@ -153,6 +157,7 @@ namespace SafeExamBrowser.Browser
|
||||
{
|
||||
var cefSharpControl = default(ICefSharpControl);
|
||||
var controlLogger = logger.CloneFor($"{nameof(BrowserControl)} #{Id}");
|
||||
var contextMenuHandler = new ContextMenuHandler();
|
||||
var dialogHandler = new DialogHandler();
|
||||
var displayHandler = new DisplayHandler();
|
||||
var downloadLogger = logger.CloneFor($"{nameof(DownloadHandler)} #{Id}");
|
||||
@@ -164,7 +169,7 @@ namespace SafeExamBrowser.Browser
|
||||
var renderHandler = new RenderProcessMessageHandler(appConfig, clipboard, keyGenerator, settings, text);
|
||||
var requestFilter = new RequestFilter();
|
||||
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}");
|
||||
var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
|
||||
var resourceHandler = new ResourceHandler(appConfig, requestFilter, integrations, keyGenerator, logger, sessionMode, settings, WindowSettings, text);
|
||||
var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings);
|
||||
|
||||
Icon = new BrowserIconResource();
|
||||
@@ -202,6 +207,7 @@ namespace SafeExamBrowser.Browser
|
||||
Control = new BrowserControl(
|
||||
clipboard,
|
||||
cefSharpControl,
|
||||
contextMenuHandler,
|
||||
dialogHandler,
|
||||
displayHandler,
|
||||
downloadHandler,
|
||||
@@ -218,12 +224,13 @@ namespace SafeExamBrowser.Browser
|
||||
Control.TitleChanged += Control_TitleChanged;
|
||||
|
||||
Control.Initialize();
|
||||
|
||||
logger.Debug("Initialized browser control.");
|
||||
}
|
||||
|
||||
internal void InitializeWindow()
|
||||
{
|
||||
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, this.logger);
|
||||
window = uiFactory.CreateBrowserWindow(Control, settings, IsMainWindow, logger);
|
||||
window.AddressChanged += Window_AddressChanged;
|
||||
window.BackwardNavigationRequested += Window_BackwardNavigationRequested;
|
||||
window.Closed += Window_Closed;
|
||||
@@ -242,6 +249,7 @@ namespace SafeExamBrowser.Browser
|
||||
window.BringToForeground();
|
||||
|
||||
Handle = window.Handle;
|
||||
InitiateCookieTraversal();
|
||||
|
||||
logger.Debug("Initialized browser window.");
|
||||
}
|
||||
@@ -256,6 +264,22 @@ namespace SafeExamBrowser.Browser
|
||||
.Build();
|
||||
}
|
||||
|
||||
private void InitiateCookieTraversal()
|
||||
{
|
||||
var visitor = new CookieVisitor(integrations);
|
||||
|
||||
visitor.UserIdentifierDetected += (id) => UserIdentifierDetected?.Invoke(id);
|
||||
|
||||
if (Cef.GetGlobalCookieManager().VisitAllCookies(visitor))
|
||||
{
|
||||
logger.Debug("Successfully initiated cookie traversal.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Failed to initiate cookie traversal!");
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeRequestFilter(IRequestFilter requestFilter)
|
||||
{
|
||||
if (settings.Filter.ProcessContentRequests || settings.Filter.ProcessMainRequests)
|
||||
@@ -297,8 +321,6 @@ namespace SafeExamBrowser.Browser
|
||||
window.UpdateTitle(address);
|
||||
TitleChanged?.Invoke(address);
|
||||
}
|
||||
|
||||
AutoFind();
|
||||
}
|
||||
|
||||
private void Control_LoadFailed(int errorCode, string errorText, bool isMainRequest, string url)
|
||||
@@ -834,14 +856,6 @@ namespace SafeExamBrowser.Browser
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoFind()
|
||||
{
|
||||
if (settings.AllowFind && !string.IsNullOrEmpty(findParameters.term) && !CLEAR_FIND_TERM.Equals(findParameters.term, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Control.Find(findParameters.term, findParameters.isInitial, findParameters.caseSensitive, findParameters.forward);
|
||||
}
|
||||
}
|
||||
|
||||
private double CalculateZoomPercentage()
|
||||
{
|
||||
return (zoomLevel * 25.0) + 100.0;
|
||||
|
@@ -30,7 +30,7 @@ namespace SafeExamBrowser.Browser
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
internal void Process(JavascriptMessageReceivedEventArgs message)
|
||||
internal void Update(JavascriptMessageReceivedEventArgs message)
|
||||
{
|
||||
if (settings.UseIsolatedClipboard)
|
||||
{
|
||||
@@ -65,7 +65,7 @@ namespace SafeExamBrowser.Browser
|
||||
private class Data
|
||||
{
|
||||
public string Content { get; set; }
|
||||
public long Id { get; set; }
|
||||
public string Id { get; set; }
|
||||
public string Type { get; set; }
|
||||
}
|
||||
}
|
||||
|
@@ -5,191 +5,207 @@
|
||||
* 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/.
|
||||
*
|
||||
* Original code taken and slightly adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215.
|
||||
* Original code taken and adapted from https://github.com/eqsoft/seb2/blob/master/browser/app/modules/SebBrowser.jsm#L1215.
|
||||
*/
|
||||
|
||||
SafeExamBrowser.clipboard = {
|
||||
id: Math.round((Date.now() + Math.random()) * 1000),
|
||||
ranges: [],
|
||||
text: "",
|
||||
|
||||
clear: function () {
|
||||
this.ranges = [];
|
||||
this.text = "";
|
||||
},
|
||||
|
||||
getContentEncoded: function () {
|
||||
var bytes = new TextEncoder().encode(this.text);
|
||||
var base64 = btoa(String.fromCodePoint(...bytes));
|
||||
|
||||
return base64;
|
||||
},
|
||||
|
||||
update: function (id, base64) {
|
||||
if (this.id != id) {
|
||||
var bytes = Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
|
||||
var content = new TextDecoder().decode(bytes);
|
||||
if (typeof SafeExamBrowser.clipboard === 'undefined') {
|
||||
SafeExamBrowser.clipboard = {
|
||||
id: crypto.randomUUID(),
|
||||
ranges: [],
|
||||
text: "",
|
||||
|
||||
clear: function () {
|
||||
this.ranges = [];
|
||||
this.text = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
this.text = "";
|
||||
},
|
||||
|
||||
function copySelectedData(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
||||
SafeExamBrowser.clipboard.ranges = [];
|
||||
} else {
|
||||
var selection = e.target.ownerDocument.defaultView.getSelection();
|
||||
var text = "";
|
||||
getContentEncoded: function () {
|
||||
var bytes = new TextEncoder().encode(this.text);
|
||||
var base64 = btoa(String.fromCodePoint(...bytes));
|
||||
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
SafeExamBrowser.clipboard.ranges[i] = selection.getRangeAt(i).cloneContents();
|
||||
text += SafeExamBrowser.clipboard.ranges[i].textContent;
|
||||
}
|
||||
return base64;
|
||||
},
|
||||
|
||||
SafeExamBrowser.clipboard.text = text;
|
||||
}
|
||||
}
|
||||
update: function (id, base64) {
|
||||
if (this.id != id) {
|
||||
var bytes = Uint8Array.from(atob(base64), (m) => m.codePointAt(0));
|
||||
var content = new TextDecoder().decode(bytes);
|
||||
|
||||
function cutSelectedData(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||
} else {
|
||||
var designMode = e.target.ownerDocument.designMode;
|
||||
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
||||
var selection = e.target.ownerDocument.defaultView.getSelection();
|
||||
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
var range = selection.getRangeAt(i);
|
||||
|
||||
if (designMode === 'on') {
|
||||
range.deleteContents();
|
||||
} else {
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
if (node.contains(range.commonAncestorContainer)) {
|
||||
range.deleteContents();
|
||||
}
|
||||
});
|
||||
}
|
||||
this.ranges = [];
|
||||
this.text = content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pasteSelectedData(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
|
||||
} else {
|
||||
var w = e.target.ownerDocument.defaultView;
|
||||
var designMode = e.target.ownerDocument.designMode;
|
||||
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
||||
var selection = w.getSelection();
|
||||
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
var r = selection.getRangeAt(i);
|
||||
|
||||
if (designMode === 'on') {
|
||||
r.deleteContents();
|
||||
} else {
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
if (node.contains(r.commonAncestorContainer)) {
|
||||
r.deleteContents();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (designMode === 'on') {
|
||||
var range = w.getSelection().getRangeAt(0);
|
||||
|
||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||
range = w.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
const newNode = r.cloneNode(true);
|
||||
range.insertNode(newNode);
|
||||
range.collapse();
|
||||
});
|
||||
} else {
|
||||
range.collapse();
|
||||
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||
range.collapse();
|
||||
}
|
||||
if (typeof copySelection === 'undefined') {
|
||||
function copySelection(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
SafeExamBrowser.clipboard.text = e.target.value.substring(e.target.selectionStart, e.target.selectionEnd);
|
||||
SafeExamBrowser.clipboard.ranges = [];
|
||||
} else {
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
var range = w.getSelection().getRangeAt(0);
|
||||
var selection = e.target.ownerDocument.defaultView.getSelection();
|
||||
var text = "";
|
||||
|
||||
if (node.contains(range.commonAncestorContainer)) {
|
||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||
range = w.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
const newNode = r.cloneNode(true);
|
||||
range.insertNode(newNode);
|
||||
range.collapse();
|
||||
});
|
||||
} else {
|
||||
range = w.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
range.insertNode(w.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||
range.collapse();
|
||||
}
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
SafeExamBrowser.clipboard.ranges[i] = selection.getRangeAt(i).cloneContents();
|
||||
text += SafeExamBrowser.clipboard.ranges[i].textContent;
|
||||
}
|
||||
|
||||
SafeExamBrowser.clipboard.text = text;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (typeof cutSelection === 'undefined') {
|
||||
function cutSelection(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||
} else {
|
||||
var designMode = e.target.ownerDocument.designMode;
|
||||
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
||||
var selection = e.target.ownerDocument.defaultView.getSelection();
|
||||
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
var range = selection.getRangeAt(i);
|
||||
|
||||
if (designMode === 'on') {
|
||||
range.deleteContents();
|
||||
} else {
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
if (node.contains(range.commonAncestorContainer)) {
|
||||
range.deleteContents();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onCopy(e) {
|
||||
SafeExamBrowser.clipboard.clear();
|
||||
if (typeof pasteContent === 'undefined') {
|
||||
function pasteContent(e) {
|
||||
if (e.target.contentEditable && e.target.setRangeText) {
|
||||
e.target.setRangeText("", e.target.selectionStart, e.target.selectionEnd, 'select');
|
||||
e.target.setRangeText(SafeExamBrowser.clipboard.text, e.target.selectionStart, e.target.selectionStart + SafeExamBrowser.clipboard.text.length, 'end');
|
||||
} else {
|
||||
var targetWindow = e.target.ownerDocument.defaultView;
|
||||
var designMode = e.target.ownerDocument.designMode;
|
||||
var contentEditables = e.target.ownerDocument.querySelectorAll('*[contenteditable]');
|
||||
var selection = targetWindow.getSelection();
|
||||
|
||||
try {
|
||||
copySelectedData(e);
|
||||
for (var i = 0; i < selection.rangeCount; i++) {
|
||||
var r = selection.getRangeAt(i);
|
||||
|
||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
e.returnValue = false;
|
||||
if (designMode === 'on') {
|
||||
r.deleteContents();
|
||||
} else {
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
if (node.contains(r.commonAncestorContainer)) {
|
||||
r.deleteContents();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (designMode === 'on') {
|
||||
var range = targetWindow.getSelection().getRangeAt(0);
|
||||
|
||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||
range = targetWindow.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
const newNode = r.cloneNode(true);
|
||||
range.insertNode(newNode);
|
||||
range.collapse();
|
||||
});
|
||||
} else {
|
||||
range.collapse();
|
||||
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||
range.collapse();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (contentEditables.length) {
|
||||
contentEditables.forEach(node => {
|
||||
var range = targetWindow.getSelection().getRangeAt(0);
|
||||
|
||||
if (node.contains(range.commonAncestorContainer)) {
|
||||
if (SafeExamBrowser.clipboard.ranges.length > 0) {
|
||||
|
||||
SafeExamBrowser.clipboard.ranges.map(r => {
|
||||
range = targetWindow.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
const newNode = r.cloneNode(true);
|
||||
range.insertNode(newNode);
|
||||
range.collapse();
|
||||
});
|
||||
} else {
|
||||
range = targetWindow.getSelection().getRangeAt(0);
|
||||
range.collapse();
|
||||
range.insertNode(targetWindow.document.createTextNode(SafeExamBrowser.clipboard.text));
|
||||
range.collapse();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function onCut(e) {
|
||||
SafeExamBrowser.clipboard.clear();
|
||||
if (typeof onCopy === 'undefined') {
|
||||
function onCopy(e) {
|
||||
try {
|
||||
SafeExamBrowser.clipboard.clear();
|
||||
|
||||
try {
|
||||
copySelectedData(e);
|
||||
cutSelectedData(e);
|
||||
copySelection(e);
|
||||
|
||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
e.returnValue = false;
|
||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
window.document.addEventListener("copy", onCopy, true);
|
||||
}
|
||||
|
||||
function onPaste(e) {
|
||||
try {
|
||||
pasteSelectedData(e);
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
e.returnValue = false;
|
||||
if (typeof onCut === 'undefined') {
|
||||
function onCut(e) {
|
||||
try {
|
||||
SafeExamBrowser.clipboard.clear();
|
||||
|
||||
copySelection(e);
|
||||
cutSelection(e);
|
||||
|
||||
CefSharp.PostMessage({ Type: "Clipboard", Id: SafeExamBrowser.clipboard.id, Content: SafeExamBrowser.clipboard.getContentEncoded() });
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
window.document.addEventListener("cut", onCut, true);
|
||||
}
|
||||
|
||||
window.document.addEventListener("copy", onCopy, true);
|
||||
window.document.addEventListener("cut", onCut, true);
|
||||
window.document.addEventListener("paste", onPaste, true);
|
||||
|
||||
if (typeof onPaste === 'undefined') {
|
||||
function onPaste(e) {
|
||||
try {
|
||||
pasteContent(e);
|
||||
} finally {
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
window.document.addEventListener("paste", onPaste, true);
|
||||
}
|
||||
|
@@ -8,5 +8,5 @@
|
||||
|
||||
namespace SafeExamBrowser.Browser.Events
|
||||
{
|
||||
internal delegate void ClipboardChangedEventHandler(long id);
|
||||
internal delegate void ClipboardChangedEventHandler(string id);
|
||||
}
|
||||
|
@@ -6,12 +6,11 @@
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||
namespace SafeExamBrowser.Browser.Events
|
||||
{
|
||||
internal class ConfigurationCompletedEventArgs : ActionRequiredEventArgs
|
||||
internal class JavaScriptDialogRequestedEventArgs
|
||||
{
|
||||
public bool AbortStartup { get; set; }
|
||||
internal bool Success { get; set; }
|
||||
internal JavaScriptDialogType Type { get; set; }
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Browser.Events
|
||||
{
|
||||
internal delegate void JavaScriptDialogRequestedEventHandler(JavaScriptDialogRequestedEventArgs args);
|
||||
}
|
16
SafeExamBrowser.Browser/Events/JavaScriptDialogType.cs
Normal file
16
SafeExamBrowser.Browser/Events/JavaScriptDialogType.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Browser.Events
|
||||
{
|
||||
internal enum JavaScriptDialogType
|
||||
{
|
||||
LeavePage,
|
||||
Reload
|
||||
}
|
||||
}
|
@@ -12,21 +12,21 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class ContextMenuHandler : IContextMenuHandler
|
||||
{
|
||||
public void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
|
||||
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
|
||||
{
|
||||
model.Clear();
|
||||
}
|
||||
|
||||
public bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
|
||||
public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame)
|
||||
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
}
|
||||
|
||||
public bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
|
||||
public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
49
SafeExamBrowser.Browser/Handlers/CookieVisitor.cs
Normal file
49
SafeExamBrowser.Browser/Handlers/CookieVisitor.cs
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class CookieVisitor : ICookieVisitor
|
||||
{
|
||||
private readonly IEnumerable<Integration> integrations;
|
||||
|
||||
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
|
||||
|
||||
internal CookieVisitor(IEnumerable<Integration> integrations)
|
||||
{
|
||||
this.integrations = integrations;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
}
|
||||
|
||||
public bool Visit(Cookie cookie, int count, int total, ref bool deleteCookie)
|
||||
{
|
||||
foreach (var integration in integrations)
|
||||
{
|
||||
var success = integration.TrySearchUserIdentifier(cookie, out var userIdentifier);
|
||||
|
||||
if (success)
|
||||
{
|
||||
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
26
SafeExamBrowser.Browser/Handlers/DragHandler.cs
Normal file
26
SafeExamBrowser.Browser/Handlers/DragHandler.cs
Normal file
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class DragHandler : IDragHandler
|
||||
{
|
||||
public bool OnDragEnter(IWebBrowser chromiumWebBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask)
|
||||
{
|
||||
return !(dragData.IsFragment && mask.HasFlag(DragOperationsMask.Move));
|
||||
}
|
||||
|
||||
public void OnDraggableRegionsChanged(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
32
SafeExamBrowser.Browser/Handlers/FocusHandler.cs
Normal file
32
SafeExamBrowser.Browser/Handlers/FocusHandler.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class FocusHandler : IFocusHandler
|
||||
{
|
||||
internal FocusHandler()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnGotFocus(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
}
|
||||
|
||||
public bool OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
52
SafeExamBrowser.Browser/Handlers/JavaScriptDialogHandler.cs
Normal file
52
SafeExamBrowser.Browser/Handlers/JavaScriptDialogHandler.cs
Normal file
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Browser.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class JavaScriptDialogHandler : IJsDialogHandler
|
||||
{
|
||||
internal event JavaScriptDialogRequestedEventHandler DialogRequested;
|
||||
|
||||
public bool OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback)
|
||||
{
|
||||
var args = new JavaScriptDialogRequestedEventArgs
|
||||
{
|
||||
Type = isReload ? JavaScriptDialogType.Reload : JavaScriptDialogType.LeavePage
|
||||
};
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
DialogRequested?.Invoke(args);
|
||||
|
||||
using (callback)
|
||||
{
|
||||
callback.Continue(args.Success);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
}
|
||||
|
||||
public bool OnJSDialog(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
@@ -7,18 +7,16 @@
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Threading.Tasks;
|
||||
using CefSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SafeExamBrowser.Browser.Content;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Integrations;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
@@ -36,6 +34,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly ContentLoader contentLoader;
|
||||
private readonly IRequestFilter filter;
|
||||
private readonly IEnumerable<Integration> integrations;
|
||||
private readonly IKeyGenerator keyGenerator;
|
||||
private readonly ILogger logger;
|
||||
private readonly SessionMode sessionMode;
|
||||
@@ -44,13 +43,13 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||
|
||||
private IResourceHandler contentHandler;
|
||||
private IResourceHandler pageHandler;
|
||||
private string userIdentifier;
|
||||
|
||||
internal event UserIdentifierDetectedEventHandler UserIdentifierDetected;
|
||||
|
||||
internal ResourceHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
IEnumerable<Integration> integrations,
|
||||
IKeyGenerator keyGenerator,
|
||||
ILogger logger,
|
||||
SessionMode sessionMode,
|
||||
@@ -60,6 +59,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.filter = filter;
|
||||
this.integrations = integrations;
|
||||
this.contentLoader = new ContentLoader(text);
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.logger = logger;
|
||||
@@ -235,217 +235,17 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||
|
||||
private void SearchUserIdentifier(IRequest request, IResponse response)
|
||||
{
|
||||
var success = TrySearchGenericUserIdentifier(response);
|
||||
|
||||
if (!success)
|
||||
foreach (var integration in integrations)
|
||||
{
|
||||
success = TrySearchEdxUserIdentifier(response);
|
||||
}
|
||||
var success = integration.TrySearchUserIdentifier(request, response, out var userIdentifier);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
TrySearchMoodleUserIdentifier(request, response);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TrySearchGenericUserIdentifier(IResponse response)
|
||||
{
|
||||
var ids = response.Headers.GetValues("X-LMS-USER-ID");
|
||||
var success = false;
|
||||
|
||||
if (ids != default(string[]))
|
||||
{
|
||||
var userId = ids.FirstOrDefault();
|
||||
|
||||
if (userId != default && userIdentifier != userId)
|
||||
if (success)
|
||||
{
|
||||
userIdentifier = userId;
|
||||
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
|
||||
logger.Info("Generic LMS user identifier detected.");
|
||||
success = true;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TrySearchEdxUserIdentifier(IResponse response)
|
||||
{
|
||||
var cookies = response.Headers.GetValues("Set-Cookie");
|
||||
var success = false;
|
||||
|
||||
if (cookies != default(string[]))
|
||||
{
|
||||
try
|
||||
{
|
||||
var userInfo = cookies.FirstOrDefault(c => c.Contains("edx-user-info"));
|
||||
|
||||
if (userInfo != default)
|
||||
{
|
||||
var start = userInfo.IndexOf("=") + 1;
|
||||
var end = userInfo.IndexOf("; expires");
|
||||
var cookie = userInfo.Substring(start, end - start);
|
||||
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
|
||||
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
|
||||
var userName = json["username"].Value<string>();
|
||||
|
||||
if (userIdentifier != userName)
|
||||
{
|
||||
userIdentifier = userName;
|
||||
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
|
||||
logger.Info("EdX user identifier detected.");
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to parse edX user identifier!", e);
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TrySearchMoodleUserIdentifier(IRequest request, IResponse response)
|
||||
{
|
||||
var success = TrySearchMoodleUserIdentifierByLocation(response);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Plugin, request, response);
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
success = TrySearchMoodleUserIdentifierByRequest(MoodleRequestType.Theme, request, response);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TrySearchMoodleUserIdentifierByLocation(IResponse response)
|
||||
{
|
||||
var locations = response.Headers.GetValues("Location");
|
||||
|
||||
if (locations != default(string[]))
|
||||
{
|
||||
try
|
||||
{
|
||||
var location = locations.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
|
||||
|
||||
if (location != default)
|
||||
{
|
||||
var userId = location.Substring(location.IndexOf("=") + 1);
|
||||
|
||||
if (userIdentifier != userId)
|
||||
{
|
||||
userIdentifier = userId;
|
||||
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
|
||||
logger.Info("Moodle user identifier detected by location.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to parse Moodle user identifier by location!", e);
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool TrySearchMoodleUserIdentifierByRequest(MoodleRequestType type, IRequest request, IResponse response)
|
||||
{
|
||||
var cookies = response.Headers.GetValues("Set-Cookie");
|
||||
var success = false;
|
||||
|
||||
if (cookies != default(string[]))
|
||||
{
|
||||
var session = cookies.FirstOrDefault(c => c.Contains("MoodleSession"));
|
||||
|
||||
if (session != default)
|
||||
{
|
||||
var userId = ExecuteMoodleUserIdentifierRequest(request.Url, session, type);
|
||||
|
||||
if (int.TryParse(userId, out var id) && id > 0 && userIdentifier != userId)
|
||||
{
|
||||
userIdentifier = userId;
|
||||
Task.Run(() => UserIdentifierDetected?.Invoke(userIdentifier));
|
||||
logger.Info($"Moodle user identifier detected by request ({type}).");
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private string ExecuteMoodleUserIdentifierRequest(string requestUrl, string session, MoodleRequestType type)
|
||||
{
|
||||
var userId = default(string);
|
||||
|
||||
try
|
||||
{
|
||||
Task.Run(async () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
var endpointUrl = default(string);
|
||||
var start = session.IndexOf("=") + 1;
|
||||
var end = session.IndexOf(";");
|
||||
var name = session.Substring(0, start - 1);
|
||||
var value = session.Substring(start, end - start);
|
||||
var uri = new Uri(requestUrl);
|
||||
|
||||
if (type == MoodleRequestType.Plugin)
|
||||
{
|
||||
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/mod/quiz/accessrule/sebserver/classes/external/user.php";
|
||||
}
|
||||
else
|
||||
{
|
||||
endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}/theme/boost_ethz/sebuser.php";
|
||||
}
|
||||
|
||||
var message = new HttpRequestMessage(HttpMethod.Get, endpointUrl);
|
||||
|
||||
using (var handler = new HttpClientHandler { UseCookies = false })
|
||||
using (var client = new HttpClient(handler))
|
||||
{
|
||||
message.Headers.Add("Cookie", $"{name}={value}");
|
||||
|
||||
var result = await client.SendAsync(message);
|
||||
|
||||
if (result.IsSuccessStatusCode)
|
||||
{
|
||||
userId = await result.Content.ReadAsStringAsync();
|
||||
}
|
||||
else if (result.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
logger.Error($"Failed to retrieve Moodle user identifier by request ({type})! Response: {(int) result.StatusCode} {result.ReasonPhrase}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to parse Moodle user identifier by request ({type})!", e);
|
||||
}
|
||||
}).GetAwaiter().GetResult();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to execute Moodle user identifier request ({type})!", e);
|
||||
}
|
||||
|
||||
return userId;
|
||||
}
|
||||
|
||||
private enum MoodleRequestType
|
||||
{
|
||||
Plugin,
|
||||
Theme
|
||||
}
|
||||
}
|
||||
}
|
||||
|
75
SafeExamBrowser.Browser/Integrations/EdxIntegration.cs
Normal file
75
SafeExamBrowser.Browser/Integrations/EdxIntegration.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using CefSharp;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Integrations
|
||||
{
|
||||
internal class EdxIntegration : Integration
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public EdxIntegration(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
|
||||
{
|
||||
userIdentifier = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
|
||||
{
|
||||
var cookies = response.Headers.GetValues("Set-Cookie");
|
||||
var userInfo = cookies?.FirstOrDefault(c => c.Contains("edx-user-info"));
|
||||
|
||||
userIdentifier = default;
|
||||
|
||||
if (TryParseCookie(userInfo, out var id) && HasChanged(id))
|
||||
{
|
||||
userIdentifier = id;
|
||||
logger.Info($"User identifier '{id}' detected by session cookie on response.");
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
|
||||
private bool TryParseCookie(string userInfo, out string userIdentifier)
|
||||
{
|
||||
userIdentifier = default;
|
||||
|
||||
try
|
||||
{
|
||||
if (userInfo != default)
|
||||
{
|
||||
var start = userInfo.IndexOf("=") + 1;
|
||||
var end = userInfo.IndexOf("; expires");
|
||||
var cookie = userInfo.Substring(start, end - start);
|
||||
var sanitized = cookie.Replace("\\\"", "\"").Replace("\\054", ",").Trim('"');
|
||||
var json = JsonConvert.DeserializeObject(sanitized) as JObject;
|
||||
|
||||
userIdentifier = json["username"].Value<string>();
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to parse user identifier!", e);
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
}
|
||||
}
|
47
SafeExamBrowser.Browser/Integrations/GenericIntegration.cs
Normal file
47
SafeExamBrowser.Browser/Integrations/GenericIntegration.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Integrations
|
||||
{
|
||||
internal class GenericIntegration : Integration
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
|
||||
public GenericIntegration(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
|
||||
{
|
||||
userIdentifier = default;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
|
||||
{
|
||||
var ids = response.Headers.GetValues("X-LMS-USER-ID");
|
||||
var id = ids?.FirstOrDefault();
|
||||
|
||||
userIdentifier = default;
|
||||
|
||||
if (HasChanged(id))
|
||||
{
|
||||
userIdentifier = id;
|
||||
logger.Info($"User identifier '{id}' detected by header of response.");
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
}
|
||||
}
|
32
SafeExamBrowser.Browser/Integrations/Integration.cs
Normal file
32
SafeExamBrowser.Browser/Integrations/Integration.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Integrations
|
||||
{
|
||||
internal abstract class Integration
|
||||
{
|
||||
private static string activeUserIdentifier;
|
||||
|
||||
internal abstract bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier);
|
||||
internal abstract bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier);
|
||||
|
||||
protected bool HasChanged(string userIdentifier)
|
||||
{
|
||||
var current = activeUserIdentifier;
|
||||
|
||||
if (userIdentifier != default && activeUserIdentifier != userIdentifier)
|
||||
{
|
||||
activeUserIdentifier = userIdentifier;
|
||||
}
|
||||
|
||||
return activeUserIdentifier != current;
|
||||
}
|
||||
}
|
||||
}
|
226
SafeExamBrowser.Browser/Integrations/MoodleIntegration.cs
Normal file
226
SafeExamBrowser.Browser/Integrations/MoodleIntegration.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using CefSharp;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using Cookie = CefSharp.Cookie;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Integrations
|
||||
{
|
||||
internal class MoodleIntegration : Integration
|
||||
{
|
||||
private const string PLUGIN_PATH = "/mod/quiz/accessrule/sebserver/classes/external/user.php";
|
||||
private const string SESSION_COOKIE_NAME = "MoodleSession";
|
||||
private const string THEME_PATH = "/theme/boost_ethz/sebuser.php";
|
||||
|
||||
private readonly ILogger logger;
|
||||
|
||||
public MoodleIntegration(ILogger logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(Cookie cookie, out string userIdentifier)
|
||||
{
|
||||
return TrySearchByCookie(cookie, out userIdentifier);
|
||||
}
|
||||
|
||||
internal override bool TrySearchUserIdentifier(IRequest request, IResponse response, out string userIdentifier)
|
||||
{
|
||||
var success = TrySearchByLocation(response, out userIdentifier);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
success = TrySearchByRequests(request, response, out userIdentifier);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TrySearchByCookie(Cookie cookie, out string userIdentifier)
|
||||
{
|
||||
var id = default(string);
|
||||
var type = default(RequestType);
|
||||
var isSession = cookie.Name.Contains(SESSION_COOKIE_NAME);
|
||||
var url = $"{(cookie.Secure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp)}{Uri.SchemeDelimiter}{cookie.Domain}{cookie.Path}";
|
||||
var hasId = isSession && TryExecuteRequests(url, (cookie.Name, cookie.Value), out type, out id);
|
||||
|
||||
userIdentifier = default;
|
||||
|
||||
if (hasId && HasChanged(id))
|
||||
{
|
||||
userIdentifier = id;
|
||||
logger.Info($"User identifier '{id}' detected by request on cookie traversal ({type}).");
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
|
||||
private bool TrySearchByLocation(IResponse response, out string userIdentifier)
|
||||
{
|
||||
var locations = response.Headers.GetValues("Location");
|
||||
var location = locations?.FirstOrDefault(l => l.Contains("/login/index.php?testsession"));
|
||||
|
||||
userIdentifier = default;
|
||||
|
||||
if (TryParseLocation(location, out var id) && HasChanged(id))
|
||||
{
|
||||
userIdentifier = id;
|
||||
logger.Info($"User identifier '{id}' detected by location header of response.");
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
|
||||
private bool TrySearchByRequests(IRequest request, IResponse response, out string userIdentifier)
|
||||
{
|
||||
var id = default(string);
|
||||
var type = default(RequestType);
|
||||
var cookies = response.Headers.GetValues("Set-Cookie");
|
||||
var session = cookies?.FirstOrDefault(c => c.Contains(SESSION_COOKIE_NAME));
|
||||
var hasCookie = TryParseCookie(session, out var cookie);
|
||||
var hasId = hasCookie && TryExecuteRequests(request.Url, cookie, out type, out id);
|
||||
|
||||
userIdentifier = default;
|
||||
|
||||
if (hasId && HasChanged(id))
|
||||
{
|
||||
userIdentifier = id;
|
||||
logger.Info($"User identifier '{id}' detected by request on response ({type}).");
|
||||
}
|
||||
|
||||
return userIdentifier != default;
|
||||
}
|
||||
|
||||
private bool TryExecuteRequests(string originUrl, (string name, string value) session, out RequestType requestType, out string userId)
|
||||
{
|
||||
var order = new[] { RequestType.Plugin, RequestType.Theme };
|
||||
|
||||
requestType = default;
|
||||
userId = default;
|
||||
|
||||
foreach (var type in order)
|
||||
{
|
||||
try
|
||||
{
|
||||
var url = BuildUrl(originUrl, type);
|
||||
|
||||
using (var response = ExecuteRequest(url, session))
|
||||
{
|
||||
if (TryParseResponse(response, type, out var id))
|
||||
{
|
||||
requestType = type;
|
||||
userId = id;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Failed to execute user identifier request ({type})!", e);
|
||||
}
|
||||
}
|
||||
|
||||
return userId != default;
|
||||
}
|
||||
|
||||
private string BuildUrl(string originUrl, RequestType type)
|
||||
{
|
||||
var uri = new Uri(originUrl);
|
||||
var endpointUrl = $"{uri.Scheme}{Uri.SchemeDelimiter}{uri.Host}{(type == RequestType.Plugin ? PLUGIN_PATH : THEME_PATH)}";
|
||||
|
||||
return endpointUrl;
|
||||
}
|
||||
|
||||
private HttpResponseMessage ExecuteRequest(string url, (string name, string value) session)
|
||||
{
|
||||
using (var message = new HttpRequestMessage(HttpMethod.Get, url))
|
||||
using (var handler = new HttpClientHandler { UseCookies = false })
|
||||
using (var client = new HttpClient(handler))
|
||||
{
|
||||
message.Headers.Add("Cookie", $"{session.name}={session.value}");
|
||||
|
||||
return client.SendAsync(message).GetAwaiter().GetResult();
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryParseCookie(string session, out (string name, string value) cookie)
|
||||
{
|
||||
cookie = default;
|
||||
|
||||
try
|
||||
{
|
||||
if (session != default)
|
||||
{
|
||||
var start = session.IndexOf("=") + 1;
|
||||
var end = session.IndexOf(";");
|
||||
|
||||
cookie.name = session.Substring(0, start - 1);
|
||||
cookie.value = session.Substring(start, end - start);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to parse session cookie!", e);
|
||||
}
|
||||
|
||||
return cookie.name != default && cookie.value != default;
|
||||
}
|
||||
|
||||
private bool TryParseLocation(string location, out string userId)
|
||||
{
|
||||
userId = default;
|
||||
|
||||
try
|
||||
{
|
||||
if (location != default)
|
||||
{
|
||||
userId = location.Substring(location.IndexOf("=") + 1);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error("Failed to parse location!", e);
|
||||
}
|
||||
|
||||
return userId != default;
|
||||
}
|
||||
|
||||
private bool TryParseResponse(HttpResponseMessage response, RequestType type, out string userId)
|
||||
{
|
||||
userId = default;
|
||||
|
||||
if (response.IsSuccessStatusCode)
|
||||
{
|
||||
var content = response.Content.ReadAsStringAsync().GetAwaiter().GetResult();
|
||||
|
||||
if (int.TryParse(content, out var id) && id > 0)
|
||||
{
|
||||
userId = content;
|
||||
}
|
||||
}
|
||||
else if (response.StatusCode != HttpStatusCode.NotFound)
|
||||
{
|
||||
logger.Error($"Failed to retrieve user identifier by request ({type})! Response: {(int) response.StatusCode} {response.ReasonPhrase}");
|
||||
}
|
||||
|
||||
return userId != default;
|
||||
}
|
||||
|
||||
private enum RequestType
|
||||
{
|
||||
Plugin,
|
||||
Theme
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,8 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" />
|
||||
<Import Project="..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props" Condition="Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -53,14 +53,14 @@
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="CefSharp, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.dll</HintPath>
|
||||
<Reference Include="CefSharp, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.Core, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.131.3.50\lib\net462\CefSharp.Core.dll</HintPath>
|
||||
<Reference Include="CefSharp.Core, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.Common.139.0.280\lib\net462\CefSharp.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="CefSharp.WinForms, Version=131.3.50.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.WinForms.131.3.50\lib\net462\CefSharp.WinForms.dll</HintPath>
|
||||
<Reference Include="CefSharp.WinForms, Version=139.0.280.0, Culture=neutral, PublicKeyToken=40c4b6fc221f4138, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\CefSharp.WinForms.139.0.280\lib\net462\CefSharp.WinForms.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>
|
||||
@@ -103,6 +103,7 @@
|
||||
<Compile Include="Handlers\ContextMenuHandler.cs" />
|
||||
<Compile Include="BrowserControl.cs" />
|
||||
<Compile Include="BrowserIconResource.cs" />
|
||||
<Compile Include="Handlers\CookieVisitor.cs" />
|
||||
<Compile Include="Handlers\DialogHandler.cs" />
|
||||
<Compile Include="Handlers\DisplayHandler.cs" />
|
||||
<Compile Include="Handlers\DownloadHandler.cs" />
|
||||
@@ -113,6 +114,10 @@
|
||||
<Compile Include="Handlers\RequestHandler.cs" />
|
||||
<Compile Include="Handlers\ResourceHandler.cs" />
|
||||
<Compile Include="Content\ContentLoader.cs" />
|
||||
<Compile Include="Integrations\EdxIntegration.cs" />
|
||||
<Compile Include="Integrations\GenericIntegration.cs" />
|
||||
<Compile Include="Integrations\Integration.cs" />
|
||||
<Compile Include="Integrations\MoodleIntegration.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Wrapper\CefSharpBrowserControl.cs">
|
||||
<SubType>Component</SubType>
|
||||
@@ -122,10 +127,13 @@
|
||||
</Compile>
|
||||
<Compile Include="Wrapper\Events\AuthCredentialsEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\BeforeBrowseEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\BeforeContextMenuEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\BeforeDownloadEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\BeforeUnloadDialogEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\CanDownloadEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\ContextCreatedEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\ContextMenuCommandEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\ContextMenuDismissedEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\ContextReleasedEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\DialogClosedEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\DownloadUpdatedEventHandler.cs" />
|
||||
@@ -144,10 +152,12 @@
|
||||
<Compile Include="Wrapper\Events\ResetDialogStateEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\ResourceRequestEventArgs.cs" />
|
||||
<Compile Include="Wrapper\Events\ResourceRequestEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\RunContextMenuEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\SetFocusEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\TakeFocusEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Events\UncaughtExceptionEventHandler.cs" />
|
||||
<Compile Include="Wrapper\Extensions.cs" />
|
||||
<Compile Include="Wrapper\Handlers\ContextMenuHandlerSwitch.cs" />
|
||||
<Compile Include="Wrapper\Handlers\DialogHandlerSwitch.cs" />
|
||||
<Compile Include="Wrapper\Handlers\DisplayHandlerSwitch.cs" />
|
||||
<Compile Include="Wrapper\Handlers\DownloadHandlerSwitch.cs" />
|
||||
@@ -227,10 +237,10 @@
|
||||
<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\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.131.3.5\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.131.3.5\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x64.139.0.28\build\chromiumembeddedframework.runtime.win-x64.props'))" />
|
||||
<Error Condition="!Exists('..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\chromiumembeddedframework.runtime.win-x86.139.0.28\build\chromiumembeddedframework.runtime.win-x86.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.props'))" />
|
||||
<Error Condition="!Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.131.3.50\build\CefSharp.Common.targets')" />
|
||||
<Import Project="..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets" Condition="Exists('..\packages\CefSharp.Common.139.0.280\build\CefSharp.Common.targets')" />
|
||||
</Project>
|
@@ -10,7 +10,6 @@ using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
using CefSharp.WinForms;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Browser.Wrapper.Events;
|
||||
using SafeExamBrowser.Browser.Wrapper.Handlers;
|
||||
|
||||
@@ -20,10 +19,13 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
{
|
||||
public event AuthCredentialsEventHandler AuthCredentialsRequired;
|
||||
public event BeforeBrowseEventHandler BeforeBrowse;
|
||||
public event BeforeContextMenuEventHandler BeforeContextMenu;
|
||||
public event BeforeDownloadEventHandler BeforeDownload;
|
||||
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
|
||||
public event CanDownloadEventHandler CanDownload;
|
||||
public event ContextCreatedEventHandler ContextCreated;
|
||||
public event ContextMenuCommandEventHandler ContextMenuCommand;
|
||||
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
|
||||
public event ContextReleasedEventHandler ContextReleased;
|
||||
public event DialogClosedEventHandler DialogClosed;
|
||||
public event DownloadUpdatedEventHandler DownloadUpdated;
|
||||
@@ -40,6 +42,7 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
public event PreKeyEventHandler PreKeyEvent;
|
||||
public event ResetDialogStateEventHandler ResetDialogState;
|
||||
public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
|
||||
public event RunContextMenuEventHandler RunContextMenu;
|
||||
public event SetFocusEventHandler SetFocus;
|
||||
public event TakeFocusEventHandler TakeFocus;
|
||||
public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
|
||||
@@ -54,7 +57,7 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
JsDialogHandler = new JavaScriptDialogHandlerSwitch();
|
||||
KeyboardHandler = new KeyboardHandlerSwitch();
|
||||
LifeSpanHandler = lifeSpanHandler;
|
||||
MenuHandler = new ContextMenuHandler();
|
||||
MenuHandler = new ContextMenuHandlerSwitch();
|
||||
RenderProcessMessageHandler = new RenderProcessMessageHandlerSwitch();
|
||||
RequestHandler = new RequestHandlerSwitch();
|
||||
}
|
||||
@@ -79,6 +82,11 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
|
||||
}
|
||||
|
||||
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
|
||||
{
|
||||
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
|
||||
}
|
||||
|
||||
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
|
||||
{
|
||||
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
|
||||
@@ -99,6 +107,16 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
ContextCreated?.Invoke(webBrowser, browser, frame);
|
||||
}
|
||||
|
||||
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
|
||||
{
|
||||
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
|
||||
}
|
||||
|
||||
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
|
||||
}
|
||||
|
||||
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
ContextReleased?.Invoke(webBrowser, browser, frame);
|
||||
@@ -174,6 +192,11 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
ResetDialogState?.Invoke(webBrowser, browser);
|
||||
}
|
||||
|
||||
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
|
||||
{
|
||||
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
|
||||
}
|
||||
|
||||
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
|
||||
{
|
||||
SetFocus?.Invoke(webBrowser, browser, source, args);
|
||||
|
@@ -18,10 +18,13 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
{
|
||||
public event AuthCredentialsEventHandler AuthCredentialsRequired;
|
||||
public event BeforeBrowseEventHandler BeforeBrowse;
|
||||
public event BeforeContextMenuEventHandler BeforeContextMenu;
|
||||
public event BeforeDownloadEventHandler BeforeDownload;
|
||||
public event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
|
||||
public event CanDownloadEventHandler CanDownload;
|
||||
public event ContextCreatedEventHandler ContextCreated;
|
||||
public event ContextMenuCommandEventHandler ContextMenuCommand;
|
||||
public event ContextMenuDismissedEventHandler ContextMenuDismissed;
|
||||
public event ContextReleasedEventHandler ContextReleased;
|
||||
public event DialogClosedEventHandler DialogClosed;
|
||||
public event DownloadUpdatedEventHandler DownloadUpdated;
|
||||
@@ -38,6 +41,7 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
public event PreKeyEventHandler PreKeyEvent;
|
||||
public event ResetDialogStateEventHandler ResetDialogState;
|
||||
public event ResourceRequestEventHandler ResourceRequestHandlerRequired;
|
||||
public event RunContextMenuEventHandler RunContextMenu;
|
||||
public event SetFocusEventHandler SetFocus;
|
||||
public event TakeFocusEventHandler TakeFocus;
|
||||
public event UncaughtExceptionEventHandler UncaughtExceptionEvent;
|
||||
@@ -70,6 +74,11 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
BeforeBrowse?.Invoke(webBrowser, browser, frame, request, userGesture, isRedirect, args);
|
||||
}
|
||||
|
||||
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
|
||||
{
|
||||
BeforeContextMenu?.Invoke(webBrowser, browser, frame, parameters, model);
|
||||
}
|
||||
|
||||
public void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args)
|
||||
{
|
||||
BeforeDownload?.Invoke(webBrowser, browser, downloadItem, callback, args);
|
||||
@@ -90,6 +99,16 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
ContextCreated?.Invoke(webBrowser, browser, frame);
|
||||
}
|
||||
|
||||
public void OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args)
|
||||
{
|
||||
ContextMenuCommand?.Invoke(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
|
||||
}
|
||||
|
||||
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
ContextMenuDismissed?.Invoke(webBrowser, browser, frame);
|
||||
}
|
||||
|
||||
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
ContextReleased?.Invoke(webBrowser, browser, frame);
|
||||
@@ -165,6 +184,11 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
ResetDialogState?.Invoke(webBrowser, browser);
|
||||
}
|
||||
|
||||
public void OnRunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args)
|
||||
{
|
||||
RunContextMenu?.Invoke(webBrowser, browser, frame, parameters, model, callback, args);
|
||||
}
|
||||
|
||||
public void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args)
|
||||
{
|
||||
SetFocus?.Invoke(webBrowser, browser, source, args);
|
||||
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void BeforeContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void BeforeUnloadDialogEventHandler(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void ContextMenuCommandEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void ContextMenuDismissedEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void DialogClosedEventHandler(IWebBrowser webBrowser, IBrowser browser);
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void DragEnterEventHandler(IWebBrowser webBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void DraggableRegionsChangedEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void GotFocusEventHandler(IWebBrowser webBrowser, IBrowser browser);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void JavaScriptDialogEventHandler(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void ResetDialogStateEventHandler(IWebBrowser webBrowser, IBrowser browser);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void RunContextMenuEventHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void SetFocusEventHandler(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args);
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Events
|
||||
{
|
||||
internal delegate void TakeFocusEventHandler(IWebBrowser chromiumWebBrowser, IBrowser browser, bool next);
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using CefSharp.WinForms.Host;
|
||||
using SafeExamBrowser.Browser.Wrapper.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Handlers
|
||||
{
|
||||
internal class ContextMenuHandlerSwitch : IContextMenuHandler
|
||||
{
|
||||
public void OnBeforeContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model)
|
||||
{
|
||||
var control = default(ICefSharpControl);
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
}
|
||||
|
||||
control?.OnBeforeContextMenu(webBrowser, browser, frame, parameters, model);
|
||||
}
|
||||
|
||||
public bool OnContextMenuCommand(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags)
|
||||
{
|
||||
var args = new GenericEventArgs();
|
||||
var control = default(ICefSharpControl);
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
}
|
||||
|
||||
control?.OnContextMenuCommand(webBrowser, browser, frame, parameters, commandId, eventFlags, args);
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
|
||||
public void OnContextMenuDismissed(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
var control = default(ICefSharpControl);
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
}
|
||||
|
||||
control?.OnContextMenuDismissed(webBrowser, browser, frame);
|
||||
}
|
||||
|
||||
public bool RunContextMenu(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback)
|
||||
{
|
||||
var args = new GenericEventArgs();
|
||||
var control = default(ICefSharpControl);
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
}
|
||||
else
|
||||
{
|
||||
control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
}
|
||||
|
||||
control?.OnRunContextMenu(webBrowser, browser, frame, parameters, model, callback, args);
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using CefSharp;
|
||||
using CefSharp.Enums;
|
||||
using CefSharp.WinForms;
|
||||
using CefSharp.WinForms.Host;
|
||||
using SafeExamBrowser.Browser.Wrapper.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Handlers
|
||||
{
|
||||
internal class DragHandlerSwitch : IDragHandler
|
||||
{
|
||||
public bool OnDragEnter(IWebBrowser webBrowser, IBrowser browser, IDragData dragData, DragOperationsMask mask)
|
||||
{
|
||||
var args = new GenericEventArgs();
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnDragEnter(webBrowser, browser, dragData, mask, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnDragEnter(webBrowser, browser, dragData, mask, args);
|
||||
}
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
|
||||
public void OnDraggableRegionsChanged(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IList<DraggableRegion> regions)
|
||||
{
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnDraggableRegionsChanged(webBrowser, browser, frame, regions);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnDraggableRegionsChanged(webBrowser, browser, frame, regions);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using CefSharp.WinForms.Host;
|
||||
using SafeExamBrowser.Browser.Wrapper.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Handlers
|
||||
{
|
||||
internal class FocusHandlerSwitch : IFocusHandler
|
||||
{
|
||||
public void OnGotFocus(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnGotFocus(webBrowser, browser);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnGotFocus(webBrowser, browser);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source)
|
||||
{
|
||||
var args = new GenericEventArgs { Value = false };
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnSetFocus(webBrowser, browser, source, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnSetFocus(webBrowser, browser, source, args);
|
||||
}
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
|
||||
public void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next)
|
||||
{
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnTakeFocus(webBrowser, browser, next);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnTakeFocus(webBrowser, browser, next);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using CefSharp;
|
||||
using CefSharp.WinForms;
|
||||
using CefSharp.WinForms.Host;
|
||||
using SafeExamBrowser.Browser.Wrapper.Events;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Wrapper.Handlers
|
||||
{
|
||||
internal class JavaScriptDialogHandlerSwitch : IJsDialogHandler
|
||||
{
|
||||
public bool OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback)
|
||||
{
|
||||
var args = new GenericEventArgs { Value = false };
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnBeforeUnloadDialog(webBrowser, browser, message, isReload, callback, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnBeforeUnloadDialog(webBrowser, browser, message, isReload, callback, args);
|
||||
}
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
|
||||
public void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnDialogClosed(webBrowser, browser);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnDialogClosed(webBrowser, browser);
|
||||
}
|
||||
}
|
||||
|
||||
public bool OnJSDialog(IWebBrowser webBrowser, IBrowser browser, string originUrl, CefJsDialogType type, string message, string promptText, IJsDialogCallback callback, ref bool suppress)
|
||||
{
|
||||
var args = new GenericEventArgs { Value = false };
|
||||
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnJavaScriptDialog(webBrowser, browser, originUrl, type, message, promptText, callback, ref suppress, args);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnJavaScriptDialog(webBrowser, browser, originUrl, type, message, promptText, callback, ref suppress, args);
|
||||
}
|
||||
|
||||
return args.Value;
|
||||
}
|
||||
|
||||
public void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser)
|
||||
{
|
||||
if (browser.IsPopup)
|
||||
{
|
||||
var control = ChromiumHostControl.FromBrowser(browser) as CefSharpPopupControl;
|
||||
|
||||
control?.OnResetDialogState(webBrowser, browser);
|
||||
}
|
||||
else
|
||||
{
|
||||
var control = ChromiumWebBrowser.FromBrowser(browser) as CefSharpBrowserControl;
|
||||
|
||||
control?.OnResetDialogState(webBrowser, browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -21,10 +21,13 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
{
|
||||
event AuthCredentialsEventHandler AuthCredentialsRequired;
|
||||
event BeforeBrowseEventHandler BeforeBrowse;
|
||||
event BeforeContextMenuEventHandler BeforeContextMenu;
|
||||
event BeforeDownloadEventHandler BeforeDownload;
|
||||
event BeforeUnloadDialogEventHandler BeforeUnloadDialog;
|
||||
event CanDownloadEventHandler CanDownload;
|
||||
event ContextCreatedEventHandler ContextCreated;
|
||||
event ContextMenuCommandEventHandler ContextMenuCommand;
|
||||
event ContextMenuDismissedEventHandler ContextMenuDismissed;
|
||||
event ContextReleasedEventHandler ContextReleased;
|
||||
event DialogClosedEventHandler DialogClosed;
|
||||
event DownloadUpdatedEventHandler DownloadUpdated;
|
||||
@@ -41,6 +44,7 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
event PreKeyEventHandler PreKeyEvent;
|
||||
event ResetDialogStateEventHandler ResetDialogState;
|
||||
event ResourceRequestEventHandler ResourceRequestHandlerRequired;
|
||||
event RunContextMenuEventHandler RunContextMenu;
|
||||
event SetFocusEventHandler SetFocus;
|
||||
event TakeFocusEventHandler TakeFocus;
|
||||
event UncaughtExceptionEventHandler UncaughtExceptionEvent;
|
||||
@@ -50,10 +54,13 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
void GetResourceRequestHandler(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool isNavigation, bool isDownload, string requestInitiator, ref bool disableDefaultHandling, ResourceRequestEventArgs args);
|
||||
void Load(string address);
|
||||
void OnBeforeBrowse(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, bool userGesture, bool isRedirect, GenericEventArgs args);
|
||||
void OnBeforeContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model);
|
||||
void OnBeforeDownload(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IBeforeDownloadCallback callback, GenericEventArgs args);
|
||||
void OnBeforeUnloadDialog(IWebBrowser webBrowser, IBrowser browser, string message, bool isReload, IJsDialogCallback callback, GenericEventArgs args);
|
||||
void OnCanDownload(IWebBrowser webBrowser, IBrowser browser, string url, string requestMethod, GenericEventArgs args);
|
||||
void OnContextCreated(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
|
||||
void OnContextMenuCommand(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, CefMenuCommand commandId, CefEventFlags eventFlags, GenericEventArgs args);
|
||||
void OnContextMenuDismissed(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame);
|
||||
void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame);
|
||||
void OnDialogClosed(IWebBrowser webBrowser, IBrowser browser);
|
||||
void OnDownloadUpdated(IWebBrowser webBrowser, IBrowser browser, DownloadItem downloadItem, IDownloadItemCallback callback);
|
||||
@@ -69,6 +76,7 @@ namespace SafeExamBrowser.Browser.Wrapper
|
||||
void OnOpenUrlFromTab(IWebBrowser webBrowser, IBrowser browser, IFrame frame, string targetUrl, WindowOpenDisposition targetDisposition, bool userGesture, GenericEventArgs args);
|
||||
void OnPreKeyEvent(IWebBrowser webBrowser, IBrowser browser, KeyType type, int windowsKeyCode, int nativeKeyCode, CefEventFlags modifiers, bool isSystemKey, ref bool isKeyboardShortcut, GenericEventArgs args);
|
||||
void OnResetDialogState(IWebBrowser webBrowser, IBrowser browser);
|
||||
void OnRunContextMenu(IWebBrowser chromiumWebBrowser, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback, GenericEventArgs args);
|
||||
void OnSetFocus(IWebBrowser webBrowser, IBrowser browser, CefFocusSource source, GenericEventArgs args);
|
||||
void OnTakeFocus(IWebBrowser webBrowser, IBrowser browser, bool next);
|
||||
void OnUncaughtException(IWebBrowser webBrowser, IBrowser browser, IFrame frame, JavascriptException exception);
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="CefSharp.Common" version="131.3.50" targetFramework="net48" />
|
||||
<package id="CefSharp.WinForms" version="131.3.50" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="131.3.5" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="131.3.5" targetFramework="net48" />
|
||||
<package id="CefSharp.Common" version="139.0.280" targetFramework="net48" />
|
||||
<package id="CefSharp.WinForms" version="139.0.280" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x64" version="139.0.28" targetFramework="net48" />
|
||||
<package id="chromiumembeddedframework.runtime.win-x86" version="139.0.28" targetFramework="net48" />
|
||||
<package id="Newtonsoft.Json" version="13.0.3" targetFramework="net48" />
|
||||
<package id="Syroot.Windows.IO.KnownFolders" version="1.3.0" targetFramework="net48" />
|
||||
<package id="System.Security.Principal.Windows" version="5.0.0" targetFramework="net48" />
|
||||
|
@@ -15,6 +15,8 @@ using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Server;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||
{
|
||||
@@ -22,26 +24,33 @@ namespace SafeExamBrowser.Client.UnitTests.Operations
|
||||
public class ServerOperationTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IActionCenter> actionCenter;
|
||||
private ClientContext context;
|
||||
private Mock<IInvigilator> invigilator;
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IServerProxy> server;
|
||||
private AppSettings settings;
|
||||
|
||||
private Mock<ITaskbar> taskbar;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
private ServerOperation sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
appConfig = new AppConfig();
|
||||
actionCenter = new Mock<IActionCenter>();
|
||||
context = new ClientContext();
|
||||
invigilator = new Mock<IInvigilator>();
|
||||
logger = new Mock<ILogger>();
|
||||
server = new Mock<IServerProxy>();
|
||||
settings = new AppSettings();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
context.AppConfig = appConfig;
|
||||
context.Settings = settings;
|
||||
|
||||
sut = new ServerOperation(context, logger.Object, server.Object);
|
||||
sut = new ServerOperation(actionCenter.Object, context, invigilator.Object, logger.Object, server.Object, taskbar.Object, uiFactory.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Applications.Contracts;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class ApplicationResponsibilityTests
|
||||
{
|
||||
private ClientContext context;
|
||||
private ApplicationsResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
|
||||
context = new ClientContext();
|
||||
sut = new ApplicationsResponsibility(context, logger.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustAutoStartApplications()
|
||||
{
|
||||
var application1 = new Mock<IApplication<IApplicationWindow>>();
|
||||
var application2 = new Mock<IApplication<IApplicationWindow>>();
|
||||
var application3 = new Mock<IApplication<IApplicationWindow>>();
|
||||
|
||||
application1.SetupGet(a => a.AutoStart).Returns(true);
|
||||
application2.SetupGet(a => a.AutoStart).Returns(false);
|
||||
application3.SetupGet(a => a.AutoStart).Returns(true);
|
||||
context.Applications.Add(application1.Object);
|
||||
context.Applications.Add(application2.Object);
|
||||
context.Applications.Add(application3.Object);
|
||||
|
||||
sut.Assume(ClientTask.AutoStartApplications);
|
||||
|
||||
application1.Verify(a => a.Start(), Times.Once);
|
||||
application2.Verify(a => a.Start(), Times.Never);
|
||||
application3.Verify(a => a.Start(), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts.Data;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class BrowserResponsibilityTests
|
||||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IBrowserApplication> browser;
|
||||
private ClientContext context;
|
||||
private Mock<ICoordinator> coordinator;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<IRuntimeProxy> runtime;
|
||||
private Mock<IServerProxy> server;
|
||||
private AppSettings settings;
|
||||
private Mock<ISplashScreen> splashScreen;
|
||||
private Mock<ITaskbar> taskbar;
|
||||
|
||||
private BrowserResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
|
||||
|
||||
appConfig = new AppConfig();
|
||||
browser = new Mock<IBrowserApplication>();
|
||||
context = new ClientContext();
|
||||
coordinator = new Mock<ICoordinator>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
runtime = new Mock<IRuntimeProxy>();
|
||||
server = new Mock<IServerProxy>();
|
||||
settings = new AppSettings();
|
||||
splashScreen = new Mock<ISplashScreen>();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
|
||||
context.AppConfig = appConfig;
|
||||
context.Browser = browser.Object;
|
||||
context.Responsibilities = responsibilities.Object;
|
||||
context.Runtime = runtime.Object;
|
||||
context.Server = server.Object;
|
||||
context.Settings = settings;
|
||||
|
||||
sut = new BrowserResponsibility(
|
||||
context,
|
||||
coordinator.Object,
|
||||
logger.Object,
|
||||
messageBox.Object,
|
||||
runtime.Object,
|
||||
splashScreen.Object,
|
||||
taskbar.Object);
|
||||
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustAutoStartBrowser()
|
||||
{
|
||||
settings.Browser.EnableBrowser = true;
|
||||
browser.SetupGet(b => b.AutoStart).Returns(true);
|
||||
|
||||
sut.Assume(ClientTask.AutoStartApplications);
|
||||
|
||||
browser.Verify(b => b.Start(), Times.Once);
|
||||
browser.Reset();
|
||||
browser.SetupGet(b => b.AutoStart).Returns(false);
|
||||
|
||||
sut.Assume(ClientTask.AutoStartApplications);
|
||||
|
||||
browser.Verify(b => b.Start(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotAutoStartBrowserIfNotEnabled()
|
||||
{
|
||||
settings.Browser.EnableBrowser = false;
|
||||
browser.SetupGet(b => b.AutoStart).Returns(true);
|
||||
|
||||
sut.Assume(ClientTask.AutoStartApplications);
|
||||
|
||||
browser.Verify(b => b.Start(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Browser_MustHandleUserIdentifierDetection()
|
||||
{
|
||||
var counter = 0;
|
||||
var identifier = "abc123";
|
||||
|
||||
settings.SessionMode = SessionMode.Server;
|
||||
server.Setup(s => s.SendUserIdentifier(It.IsAny<string>())).Returns(() => new ServerResponse(++counter == 3));
|
||||
|
||||
browser.Raise(b => b.UserIdentifierDetected += null, identifier);
|
||||
|
||||
server.Verify(s => s.SendUserIdentifier(It.Is<string>(id => id == identifier)), Times.Exactly(3));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Browser_MustTerminateIfRequested()
|
||||
{
|
||||
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
|
||||
browser.Raise(b => b.TerminationRequested += null);
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustAllowIfNoQuitPasswordSet()
|
||||
{
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustNotAllowWithQuitPasswordAndNoUrl()
|
||||
{
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
settings.Security.QuitPasswordHash = "abc123";
|
||||
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtime.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);
|
||||
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustAllowIfUrlMatches()
|
||||
{
|
||||
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";
|
||||
runtime.Setup(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>())).Returns(new CommunicationResult(true));
|
||||
|
||||
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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustDenyIfNotAllowed()
|
||||
{
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
settings.Security.AllowReconfiguration = false;
|
||||
settings.Security.QuitPasswordHash = "abc123";
|
||||
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustDenyIfUrlDoesNotMatch()
|
||||
{
|
||||
var args = new DownloadEventArgs { Url = "sebs://www.somehost.org/some/path/some_configuration.seb?query=123" };
|
||||
|
||||
settings.Security.AllowReconfiguration = false;
|
||||
settings.Security.QuitPasswordHash = "abc123";
|
||||
settings.Security.ReconfigurationUrl = "sebs://www.somehost.org/some/path/other_configuration.seb?query=123";
|
||||
|
||||
browser.Raise(b => b.ConfigurationDownloadRequested += null, "filepath.seb", args);
|
||||
|
||||
coordinator.Verify(c => c.RequestReconfigurationLock(), Times.Never);
|
||||
coordinator.Verify(c => c.ReleaseReconfigurationLock(), Times.Never);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
|
||||
Assert.IsFalse(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustCorrectlyHandleDownload()
|
||||
{
|
||||
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
|
||||
var downloadUrl = @"https://www.host.abc/someresource.seb";
|
||||
var filename = "filepath.seb";
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.Is<string>(p => p == downloadPath), It.Is<string>(u => u == downloadUrl)), Times.Once);
|
||||
|
||||
Assert.AreEqual(downloadPath, args.DownloadPath);
|
||||
Assert.IsTrue(args.AllowDownload);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustCorrectlyHandleFailedDownload()
|
||||
{
|
||||
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
|
||||
var downloadUrl = @"https://www.host.abc/someresource.seb";
|
||||
var filename = "filepath.seb";
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(true));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Reconfiguration_MustCorrectlyHandleFailedRequest()
|
||||
{
|
||||
var downloadPath = @"C:\Folder\Does\Not\Exist\filepath.seb";
|
||||
var downloadUrl = @"https://www.host.abc/someresource.seb";
|
||||
var filename = "filepath.seb";
|
||||
var args = new DownloadEventArgs();
|
||||
|
||||
appConfig.TemporaryDirectory = @"C:\Folder\Does\Not\Exist";
|
||||
coordinator.Setup(c => c.RequestReconfigurationLock()).Returns(true);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestReconfiguration(
|
||||
It.Is<string>(p => p == downloadPath),
|
||||
It.Is<string>(u => u == downloadUrl))).Returns(new CommunicationResult(false));
|
||||
settings.Security.AllowReconfiguration = true;
|
||||
|
||||
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.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);
|
||||
runtime.Verify(r => r.RequestReconfiguration(It.IsAny<string>(), It.IsAny<string>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailIfDependencyIsNull()
|
||||
{
|
||||
context.Browser = null;
|
||||
sut.Assume(ClientTask.DeregisterEvents);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class CommunicationResponsibilityTests
|
||||
{
|
||||
private Mock<IClientHost> clientHost;
|
||||
private ClientContext context;
|
||||
private Mock<ICoordinator> coordinator;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<IRuntimeProxy> runtimeProxy;
|
||||
private Mock<Action> shutdown;
|
||||
private Mock<ISplashScreen> splashScreen;
|
||||
private Mock<IText> text;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
||||
private CommunicationResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
|
||||
clientHost = new Mock<IClientHost>();
|
||||
context = new ClientContext();
|
||||
coordinator = new Mock<ICoordinator>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
runtimeProxy = new Mock<IRuntimeProxy>();
|
||||
shutdown = new Mock<Action>();
|
||||
splashScreen = new Mock<ISplashScreen>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
context.ClientHost = clientHost.Object;
|
||||
|
||||
sut = new CommunicationResponsibility(
|
||||
context,
|
||||
coordinator.Object,
|
||||
logger.Object,
|
||||
messageBox.Object,
|
||||
runtimeProxy.Object,
|
||||
shutdown.Object,
|
||||
splashScreen.Object,
|
||||
text.Object,
|
||||
uiFactory.Object);
|
||||
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyHandleExamSelection()
|
||||
{
|
||||
var args = new ExamSelectionRequestEventArgs
|
||||
{
|
||||
Exams = new List<(string id, string lms, string name, string url)> { ("", "", "", "") },
|
||||
RequestId = Guid.NewGuid()
|
||||
};
|
||||
var dialog = new Mock<IExamSelectionDialog>();
|
||||
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(new ExamSelectionDialogResult { Success = true });
|
||||
uiFactory.Setup(f => f.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>())).Returns(dialog.Object);
|
||||
|
||||
clientHost.Raise(c => c.ExamSelectionRequested += null, args);
|
||||
|
||||
runtimeProxy.Verify(p => p.SubmitExamSelectionResult(It.Is<Guid>(g => g == args.RequestId), true, null), Times.Once);
|
||||
uiFactory.Verify(f => f.CreateExamSelectionDialog(It.IsAny<IEnumerable<Exam>>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyHandleMessageBoxRequest()
|
||||
{
|
||||
var args = new MessageBoxRequestEventArgs
|
||||
{
|
||||
Action = (int) MessageBoxAction.YesNo,
|
||||
Icon = (int) MessageBoxIcon.Question,
|
||||
Message = "Some question to be answered",
|
||||
RequestId = Guid.NewGuid(),
|
||||
Title = "A Title"
|
||||
};
|
||||
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.Is<string>(s => s == args.Message),
|
||||
It.Is<string>(s => s == args.Title),
|
||||
It.Is<MessageBoxAction>(a => a == (MessageBoxAction) args.Action),
|
||||
It.Is<MessageBoxIcon>(i => i == (MessageBoxIcon) args.Icon),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.No);
|
||||
|
||||
clientHost.Raise(c => c.MessageBoxRequested += null, args);
|
||||
|
||||
runtimeProxy.Verify(p => p.SubmitMessageBoxResult(
|
||||
It.Is<Guid>(g => g == args.RequestId),
|
||||
It.Is<int>(r => r == (int) MessageBoxResult.No)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyHandlePasswordRequest()
|
||||
{
|
||||
var args = new PasswordRequestEventArgs
|
||||
{
|
||||
Purpose = PasswordRequestPurpose.LocalSettings,
|
||||
RequestId = Guid.NewGuid()
|
||||
};
|
||||
var dialog = new Mock<IPasswordDialog>();
|
||||
var result = new PasswordDialogResult { Password = "blubb", Success = true };
|
||||
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(result);
|
||||
uiFactory.Setup(f => f.CreatePasswordDialog(It.IsAny<string>(), It.IsAny<string>())).Returns(dialog.Object);
|
||||
|
||||
clientHost.Raise(c => c.PasswordRequested += null, args);
|
||||
|
||||
runtimeProxy.Verify(p => p.SubmitPassword(
|
||||
It.Is<Guid>(g => g == args.RequestId),
|
||||
It.Is<bool>(b => b == result.Success),
|
||||
It.Is<string>(s => s == result.Password)), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyHandleAbortedReconfiguration()
|
||||
{
|
||||
clientHost.Raise(c => c.ReconfigurationAborted += null);
|
||||
|
||||
splashScreen.Verify(s => s.Hide(), Times.AtLeastOnce);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustInformUserAboutDeniedReconfiguration()
|
||||
{
|
||||
var args = new ReconfigurationEventArgs
|
||||
{
|
||||
ConfigurationPath = @"C:\Some\File\Path.seb"
|
||||
};
|
||||
|
||||
clientHost.Raise(c => c.ReconfigurationDenied += null, args);
|
||||
|
||||
messageBox.Verify(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyHandleServerCommunicationFailure()
|
||||
{
|
||||
var args = new ServerFailureActionRequestEventArgs { RequestId = Guid.NewGuid() };
|
||||
var dialog = new Mock<IServerFailureDialog>();
|
||||
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(new ServerFailureDialogResult());
|
||||
uiFactory.Setup(f => f.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>())).Returns(dialog.Object);
|
||||
|
||||
clientHost.Raise(c => c.ServerFailureActionRequested += null, args);
|
||||
|
||||
runtimeProxy.Verify(r => r.SubmitServerFailureActionResult(It.Is<Guid>(g => g == args.RequestId), It.IsAny<bool>(), It.IsAny<bool>(), It.IsAny<bool>()), Times.Once);
|
||||
uiFactory.Verify(f => f.CreateServerFailureDialog(It.IsAny<string>(), It.IsAny<bool>()), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustCorrectlyInitiateShutdown()
|
||||
{
|
||||
clientHost.Raise(c => c.Shutdown += null);
|
||||
|
||||
shutdown.Verify(s => s(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Communication_MustShutdownOnLostConnection()
|
||||
{
|
||||
runtimeProxy.Raise(p => p.ConnectionLost += null);
|
||||
|
||||
messageBox.Verify(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>()), Times.Once);
|
||||
shutdown.Verify(s => s(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailIfDependencyIsNull()
|
||||
{
|
||||
context.ClientHost = null;
|
||||
sut.Assume(ClientTask.DeregisterEvents);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class IntegrityResponsibilityTests
|
||||
{
|
||||
private Mock<IText> text;
|
||||
private Mock<IIntegrityModule> integrityModule;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var context = new ClientContext();
|
||||
var logger = new Mock<ILogger>();
|
||||
var valid = true;
|
||||
|
||||
text = new Mock<IText>();
|
||||
integrityModule = new Mock<IIntegrityModule>();
|
||||
|
||||
integrityModule.Setup(m => m.TryVerifySessionIntegrity(It.IsAny<string>(), It.IsAny<string>(), out valid)).Returns(true);
|
||||
|
||||
var sut = new IntegrityResponsibility(context, logger.Object, text.Object);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,448 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Microsoft.Win32;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Applications;
|
||||
using SafeExamBrowser.Monitoring.Contracts.Display;
|
||||
using SafeExamBrowser.Monitoring.Contracts.System;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.Settings.Monitoring;
|
||||
using SafeExamBrowser.Settings.UserInterface;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
using IWindow = SafeExamBrowser.UserInterface.Contracts.Windows.IWindow;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class MonitoringResponsibilityTests
|
||||
{
|
||||
private Mock<IActionCenter> actionCenter;
|
||||
private Mock<IApplicationMonitor> applicationMonitor;
|
||||
private ClientContext context;
|
||||
private Mock<ICoordinator> coordinator;
|
||||
private Mock<IDisplayMonitor> displayMonitor;
|
||||
private Mock<IExplorerShell> explorerShell;
|
||||
private Mock<IHashAlgorithm> hashAlgorithm;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<IRuntimeProxy> runtime;
|
||||
private Mock<ISystemSentinel> sentinel;
|
||||
private AppSettings settings;
|
||||
private Mock<ITaskbar> taskbar;
|
||||
private Mock<IText> text;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
||||
private MonitoringResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
|
||||
|
||||
actionCenter = new Mock<IActionCenter>();
|
||||
applicationMonitor = new Mock<IApplicationMonitor>();
|
||||
context = new ClientContext();
|
||||
coordinator = new Mock<ICoordinator>();
|
||||
displayMonitor = new Mock<IDisplayMonitor>();
|
||||
explorerShell = new Mock<IExplorerShell>();
|
||||
hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
runtime = new Mock<IRuntimeProxy>();
|
||||
sentinel = new Mock<ISystemSentinel>();
|
||||
settings = new AppSettings();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
context.HashAlgorithm = hashAlgorithm.Object;
|
||||
context.MessageBox = messageBox.Object;
|
||||
context.Responsibilities = responsibilities.Object;
|
||||
context.Runtime = runtime.Object;
|
||||
context.Settings = settings;
|
||||
context.UserInterfaceFactory = uiFactory.Object;
|
||||
|
||||
sut = new MonitoringResponsibility(
|
||||
actionCenter.Object,
|
||||
applicationMonitor.Object,
|
||||
context,
|
||||
coordinator.Object,
|
||||
displayMonitor.Object,
|
||||
explorerShell.Object,
|
||||
logger.Object,
|
||||
sentinel.Object,
|
||||
taskbar.Object,
|
||||
text.Object);
|
||||
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
sut.Assume(ClientTask.StartMonitoring);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustCorrectlyHandleExplorerStartWithTaskbar()
|
||||
{
|
||||
var boundsActionCenter = 0;
|
||||
var boundsTaskbar = 0;
|
||||
var height = 30;
|
||||
var order = 0;
|
||||
var shell = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
|
||||
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == height))).Callback(() => workingArea = ++order);
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
|
||||
|
||||
applicationMonitor.Raise(a => a.ExplorerStarted += null);
|
||||
|
||||
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
|
||||
explorerShell.Verify(e => e.Terminate(), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Never);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Once);
|
||||
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
|
||||
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Once);
|
||||
|
||||
Assert.IsTrue(shell == 1);
|
||||
Assert.IsTrue(workingArea == 2);
|
||||
Assert.IsTrue(boundsActionCenter == 3);
|
||||
Assert.IsTrue(boundsTaskbar == 4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustCorrectlyHandleExplorerStartWithoutTaskbar()
|
||||
{
|
||||
var boundsActionCenter = 0;
|
||||
var boundsTaskbar = 0;
|
||||
var height = 30;
|
||||
var order = 0;
|
||||
var shell = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
actionCenter.Setup(a => a.InitializeBounds()).Callback(() => boundsActionCenter = ++order);
|
||||
explorerShell.Setup(e => e.Terminate()).Callback(() => shell = ++order);
|
||||
displayMonitor.Setup(w => w.InitializePrimaryDisplay(It.Is<int>(h => h == 0))).Callback(() => workingArea = ++order);
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
|
||||
|
||||
applicationMonitor.Raise(a => a.ExplorerStarted += null);
|
||||
|
||||
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
|
||||
explorerShell.Verify(e => e.Terminate(), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Never);
|
||||
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
|
||||
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Never);
|
||||
|
||||
Assert.IsTrue(shell == 1);
|
||||
Assert.IsTrue(workingArea == 2);
|
||||
Assert.IsTrue(boundsActionCenter == 3);
|
||||
Assert.IsTrue(boundsTaskbar == 4);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustPermitApplicationIfChosenByUserAfterFailedTermination()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult();
|
||||
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory
|
||||
.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>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.First().Id);
|
||||
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustRequestShutdownIfChosenByUserAfterFailedTermination()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult();
|
||||
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtime.Setup(p => p.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory
|
||||
.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>, LockScreenSettings>((m, t, o, s) => result.OptionId = o.Last().Id);
|
||||
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustShowLockScreenIfTerminationFailed()
|
||||
{
|
||||
var activator1 = new Mock<IActivator>();
|
||||
var activator2 = new Mock<IActivator>();
|
||||
var activator3 = new Mock<IActivator>();
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult();
|
||||
var order = 0;
|
||||
var pause = 0;
|
||||
var show = 0;
|
||||
var wait = 0;
|
||||
var close = 0;
|
||||
var resume = 0;
|
||||
|
||||
activator1.Setup(a => a.Pause()).Callback(() => pause = ++order);
|
||||
activator1.Setup(a => a.Resume()).Callback(() => resume = ++order);
|
||||
context.Activators.Add(activator1.Object);
|
||||
context.Activators.Add(activator2.Object);
|
||||
context.Activators.Add(activator3.Object);
|
||||
lockScreen.Setup(l => l.Show()).Callback(() => show = ++order);
|
||||
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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
||||
activator1.Verify(a => a.Pause(), Times.Once);
|
||||
activator1.Verify(a => a.Resume(), Times.Once);
|
||||
activator2.Verify(a => a.Pause(), Times.Once);
|
||||
activator2.Verify(a => a.Resume(), Times.Once);
|
||||
activator3.Verify(a => a.Pause(), Times.Once);
|
||||
activator3.Verify(a => a.Resume(), Times.Once);
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
lockScreen.Verify(l => l.WaitForResult(), Times.Once);
|
||||
lockScreen.Verify(l => l.Close(), Times.Once);
|
||||
|
||||
Assert.IsTrue(pause == 1);
|
||||
Assert.IsTrue(show == 2);
|
||||
Assert.IsTrue(wait == 3);
|
||||
Assert.IsTrue(close == 4);
|
||||
Assert.IsTrue(resume == 5);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ApplicationMonitor_MustValidateQuitPasswordIfTerminationFailed()
|
||||
{
|
||||
var hash = "12345";
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult { Password = "test" };
|
||||
var attempt = 0;
|
||||
var correct = new Random().Next(1, 50);
|
||||
var lockScreenResult = new Func<LockScreenResult>(() => ++attempt == correct ? result : new LockScreenResult());
|
||||
|
||||
context.Settings.Security.QuitPasswordHash = hash;
|
||||
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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
applicationMonitor.Raise(m => m.TerminationFailed += null, new List<RunningApplication>());
|
||||
|
||||
hashAlgorithm.Verify(a => a.GenerateHashFor(It.Is<string>(p => p == result.Password)), Times.Once);
|
||||
hashAlgorithm.Verify(a => a.GenerateHashFor(It.Is<string>(p => p != result.Password)), Times.Exactly(attempt - 1));
|
||||
messageBox.Verify(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.Is<IWindow>(w => w == lockScreen.Object)), Times.Exactly(attempt - 1));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DisplayMonitor_MustCorrectlyHandleDisplayChangeWithTaskbar()
|
||||
{
|
||||
var boundsActionCenter = 0;
|
||||
var boundsTaskbar = 0;
|
||||
var height = 25;
|
||||
var order = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
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);
|
||||
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
|
||||
|
||||
displayMonitor.Raise(d => d.DisplayChanged += null);
|
||||
|
||||
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Never);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Once);
|
||||
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Once);
|
||||
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
|
||||
|
||||
Assert.IsTrue(workingArea == 1);
|
||||
Assert.IsTrue(boundsActionCenter == 2);
|
||||
Assert.IsTrue(boundsTaskbar == 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DisplayMonitor_MustCorrectlyHandleDisplayChangeWithoutTaskbar()
|
||||
{
|
||||
var boundsActionCenter = 0;
|
||||
var boundsTaskbar = 0;
|
||||
var height = 25;
|
||||
var order = 0;
|
||||
var workingArea = 0;
|
||||
|
||||
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);
|
||||
displayMonitor.Setup(m => m.ValidateConfiguration(It.IsAny<DisplaySettings>())).Returns(new ValidationResult { IsAllowed = true });
|
||||
taskbar.Setup(t => t.GetAbsoluteHeight()).Returns(height);
|
||||
taskbar.Setup(t => t.InitializeBounds()).Callback(() => boundsTaskbar = ++order);
|
||||
|
||||
displayMonitor.Raise(d => d.DisplayChanged += null);
|
||||
|
||||
actionCenter.Verify(a => a.InitializeBounds(), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == 0)), Times.Once);
|
||||
displayMonitor.Verify(d => d.InitializePrimaryDisplay(It.Is<int>(h => h == height)), Times.Never);
|
||||
taskbar.Verify(t => t.GetAbsoluteHeight(), Times.Never);
|
||||
taskbar.Verify(t => t.InitializeBounds(), Times.Once);
|
||||
|
||||
Assert.IsTrue(workingArea == 1);
|
||||
Assert.IsTrue(boundsActionCenter == 2);
|
||||
Assert.IsTrue(boundsTaskbar == 3);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void DisplayMonitor_MustShowLockScreenOnDisplayChange()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
|
||||
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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
displayMonitor.Raise(d => d.DisplayChanged += null);
|
||||
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SystemMonitor_MustShowLockScreenOnSessionSwitch()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
|
||||
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
|
||||
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);
|
||||
|
||||
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleDisconnect);
|
||||
|
||||
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SystemMonitor_MustTerminateIfRequestedByUser()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
var result = new LockScreenResult();
|
||||
|
||||
coordinator.Setup(c => c.RequestSessionLock()).Returns(true);
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(result);
|
||||
runtime.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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Callback(new Action<string, string, IEnumerable<LockScreenOption>, LockScreenSettings>((message, title, options, settings) => result.OptionId = options.Last().Id))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
|
||||
|
||||
coordinator.Verify(c => c.RequestSessionLock(), Times.Once);
|
||||
coordinator.Verify(c => c.ReleaseSessionLock(), Times.Once);
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SystemMonitor_MustDoNothingIfSessionSwitchAllowed()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
|
||||
settings.Service.IgnoreService = false;
|
||||
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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
|
||||
|
||||
lockScreen.Verify(l => l.Show(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SystemMonitor_MustDoNothingIfSessionLockUnlockWithoutService()
|
||||
{
|
||||
var lockScreen = new Mock<ILockScreen>();
|
||||
|
||||
settings.Service.IgnoreService = true;
|
||||
lockScreen.Setup(l => l.WaitForResult()).Returns(new LockScreenResult());
|
||||
uiFactory
|
||||
.Setup(f => f.CreateLockScreen(It.IsAny<string>(), It.IsAny<string>(), It.IsAny<IEnumerable<LockScreenOption>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.SessionLock);
|
||||
|
||||
lockScreen.Verify(l => l.Show(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void SystemMonitor_MustShowLockscreenIfNotSessionLockUnlockWithoutService()
|
||||
{
|
||||
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>>(), It.IsAny<LockScreenSettings>()))
|
||||
.Returns(lockScreen.Object);
|
||||
|
||||
sentinel.Raise(s => s.SessionChanged += null, SessionSwitchReason.ConsoleConnect);
|
||||
|
||||
lockScreen.Verify(l => l.Show(), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class NetworkResponsibilityTests
|
||||
{
|
||||
private Mock<INetworkAdapter> networkAdapter;
|
||||
private Mock<IText> text;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var context = new ClientContext();
|
||||
var logger = new Mock<ILogger>();
|
||||
|
||||
networkAdapter = new Mock<INetworkAdapter>();
|
||||
text = new Mock<IText>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
var sut = new NetworkResponsibility(context, logger.Object, networkAdapter.Object, text.Object, uiFactory.Object);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class ProctoringResponsibilityTests
|
||||
{
|
||||
private ClientContext context;
|
||||
private Mock<ILogger> logger;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
||||
private ProctoringResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
context = new ClientContext();
|
||||
logger = new Mock<ILogger>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
sut = new ProctoringResponsibility(context, logger.Object, messageBox.Object, uiFactory.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailIfDependencyIsNull()
|
||||
{
|
||||
context.Proctoring = null;
|
||||
sut.Assume(ClientTask.PrepareShutdown_Wave1);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class ServerResponsibilityTests
|
||||
{
|
||||
private ClientContext context;
|
||||
private Mock<ICoordinator> coordinator;
|
||||
private Mock<IRuntimeProxy> runtime;
|
||||
private Mock<IServerProxy> server;
|
||||
private Mock<IText> text;
|
||||
|
||||
private ServerResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
|
||||
|
||||
context = new ClientContext();
|
||||
coordinator = new Mock<ICoordinator>();
|
||||
runtime = new Mock<IRuntimeProxy>();
|
||||
server = new Mock<IServerProxy>();
|
||||
text = new Mock<IText>();
|
||||
|
||||
context.Responsibilities = responsibilities.Object;
|
||||
context.Runtime = runtime.Object;
|
||||
context.Server = server.Object;
|
||||
|
||||
sut = new ServerResponsibility(context, coordinator.Object, logger.Object, text.Object);
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Server_MustInitiateShutdownOnEvent()
|
||||
{
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
|
||||
server.Raise(s => s.TerminationRequested += null);
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void MustNotFailIfDependencyIsNull()
|
||||
{
|
||||
context.Server = null;
|
||||
sut.Assume(ClientTask.DeregisterEvents);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,285 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using Moq;
|
||||
using SafeExamBrowser.Client.Responsibilities;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.UnitTests.Responsibilities
|
||||
{
|
||||
[TestClass]
|
||||
public class ShellResponsibilityTests
|
||||
{
|
||||
private Mock<IActionCenter> actionCenter;
|
||||
private ClientContext context;
|
||||
private Mock<IHashAlgorithm> hashAlgorithm;
|
||||
private Mock<IMessageBox> messageBox;
|
||||
private Mock<IRuntimeProxy> runtime;
|
||||
private AppSettings settings;
|
||||
private Mock<ITaskbar> taskbar;
|
||||
private Mock<IUserInterfaceFactory> uiFactory;
|
||||
|
||||
private ShellResponsibility sut;
|
||||
|
||||
[TestInitialize]
|
||||
public void Initialize()
|
||||
{
|
||||
var logger = new Mock<ILogger>();
|
||||
var responsibilities = new Mock<IResponsibilityCollection<ClientTask>>();
|
||||
|
||||
actionCenter = new Mock<IActionCenter>();
|
||||
context = new ClientContext();
|
||||
hashAlgorithm = new Mock<IHashAlgorithm>();
|
||||
messageBox = new Mock<IMessageBox>();
|
||||
runtime = new Mock<IRuntimeProxy>();
|
||||
settings = new AppSettings();
|
||||
taskbar = new Mock<ITaskbar>();
|
||||
uiFactory = new Mock<IUserInterfaceFactory>();
|
||||
|
||||
context.HashAlgorithm = hashAlgorithm.Object;
|
||||
context.MessageBox = messageBox.Object;
|
||||
context.Responsibilities = responsibilities.Object;
|
||||
context.Runtime = runtime.Object;
|
||||
context.Settings = settings;
|
||||
|
||||
sut = new ShellResponsibility(
|
||||
actionCenter.Object,
|
||||
context,
|
||||
hashAlgorithm.Object,
|
||||
logger.Object,
|
||||
messageBox.Object,
|
||||
taskbar.Object,
|
||||
uiFactory.Object);
|
||||
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustAskUserToConfirm()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
Assert.IsFalse(args.Cancel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustNotInitiateIfNotWishedByUser()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.No);
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Never);
|
||||
Assert.IsTrue(args.Cancel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustCloseActionCenterAndTaskbarIfEnabled()
|
||||
{
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
|
||||
sut.Assume(ClientTask.CloseShell);
|
||||
|
||||
actionCenter.Verify(a => a.Close(), Times.Once);
|
||||
taskbar.Verify(o => o.Close(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustNotCloseActionCenterAndTaskbarIfNotEnabled()
|
||||
{
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
|
||||
sut.Assume(ClientTask.CloseShell);
|
||||
|
||||
actionCenter.Verify(a => a.Close(), Times.Never);
|
||||
taskbar.Verify(o => o.Close(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustShowErrorMessageOnCommunicationFailure()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(false));
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
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);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustAskUserForQuitPassword()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
var dialog = new Mock<IPasswordDialog>();
|
||||
var dialogResult = new PasswordDialogResult { Password = "blobb", Success = true };
|
||||
|
||||
settings.Security.QuitPasswordHash = "1234";
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
|
||||
hashAlgorithm.Setup(h => h.GenerateHashFor(It.Is<string>(s => s == dialogResult.Password))).Returns(settings.Security.QuitPasswordHash);
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
|
||||
Assert.IsFalse(args.Cancel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustAbortAskingUserForQuitPassword()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
var dialog = new Mock<IPasswordDialog>();
|
||||
var dialogResult = new PasswordDialogResult { Success = false };
|
||||
|
||||
settings.Security.QuitPasswordHash = "1234";
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Never);
|
||||
|
||||
Assert.IsTrue(args.Cancel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Shutdown_MustNotInitiateIfQuitPasswordIncorrect()
|
||||
{
|
||||
var args = new System.ComponentModel.CancelEventArgs();
|
||||
var dialog = new Mock<IPasswordDialog>();
|
||||
var dialogResult = new PasswordDialogResult { Password = "blobb", Success = true };
|
||||
|
||||
settings.Security.QuitPasswordHash = "1234";
|
||||
dialog.Setup(d => d.Show(It.IsAny<IWindow>())).Returns(dialogResult);
|
||||
hashAlgorithm.Setup(h => h.GenerateHashFor(It.IsAny<string>())).Returns("9876");
|
||||
uiFactory.Setup(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>())).Returns(dialog.Object);
|
||||
|
||||
taskbar.Raise(t => t.QuitButtonClicked += null, args as object);
|
||||
|
||||
messageBox.Verify(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.Is<MessageBoxIcon>(i => i == MessageBoxIcon.Warning),
|
||||
It.IsAny<IWindow>()), Times.Once);
|
||||
uiFactory.Verify(u => u.CreatePasswordDialog(It.IsAny<TextKey>(), It.IsAny<TextKey>()), Times.Once);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Never);
|
||||
|
||||
Assert.IsTrue(args.Cancel);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Startup_MustCorrectlyShowTaskbar()
|
||||
{
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = true;
|
||||
sut.Assume(ClientTask.ShowShell);
|
||||
taskbar.Verify(t => t.Show(), Times.Once);
|
||||
|
||||
taskbar.Reset();
|
||||
settings.UserInterface.Taskbar.EnableTaskbar = false;
|
||||
sut.Assume(ClientTask.ShowShell);
|
||||
|
||||
taskbar.Verify(t => t.Show(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Startup_MustCorrectlyShowActionCenter()
|
||||
{
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = true;
|
||||
sut.Assume(ClientTask.ShowShell);
|
||||
|
||||
actionCenter.Verify(t => t.Promote(), Times.Once);
|
||||
actionCenter.Verify(t => t.Show(), Times.Never);
|
||||
|
||||
actionCenter.Reset();
|
||||
settings.UserInterface.ActionCenter.EnableActionCenter = false;
|
||||
sut.Assume(ClientTask.ShowShell);
|
||||
|
||||
actionCenter.Verify(t => t.Promote(), Times.Never);
|
||||
actionCenter.Verify(t => t.Show(), Times.Never);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void TerminationActivator_MustCorrectlyInitiateShutdown()
|
||||
{
|
||||
var order = 0;
|
||||
var pause = 0;
|
||||
var resume = 0;
|
||||
var terminationActivator = new Mock<ITerminationActivator>();
|
||||
|
||||
context.Activators.Add(terminationActivator.Object);
|
||||
messageBox.Setup(m => m.Show(
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<TextKey>(),
|
||||
It.IsAny<MessageBoxAction>(),
|
||||
It.IsAny<MessageBoxIcon>(),
|
||||
It.IsAny<IWindow>())).Returns(MessageBoxResult.Yes);
|
||||
runtime.Setup(r => r.RequestShutdown()).Returns(new CommunicationResult(true));
|
||||
terminationActivator.Setup(t => t.Pause()).Callback(() => pause = ++order);
|
||||
terminationActivator.Setup(t => t.Resume()).Callback(() => resume = ++order);
|
||||
|
||||
sut.Assume(ClientTask.RegisterEvents);
|
||||
terminationActivator.Raise(t => t.Activated += null);
|
||||
|
||||
Assert.AreEqual(1, pause);
|
||||
Assert.AreEqual(2, resume);
|
||||
terminationActivator.Verify(t => t.Pause(), Times.Once);
|
||||
terminationActivator.Verify(t => t.Resume(), Times.Once);
|
||||
runtime.Verify(p => p.RequestShutdown(), Times.Once);
|
||||
}
|
||||
}
|
||||
}
|
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
|
||||
@@ -61,89 +61,90 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
<HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath>
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.FileSystem.Primitives, Version=4.0.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.FileSystem.Primitives.4.3.0\lib\net46\System.IO.FileSystem.Primitives.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="System.IO.Packaging, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Packaging.9.0.1\lib\net462\System.IO.Packaging.dll</HintPath>
|
||||
<Reference Include="System.IO.Packaging, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.IO.Packaging.9.0.8\lib\net462\System.IO.Packaging.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="WindowsBase" />
|
||||
@@ -254,11 +255,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -266,13 +264,19 @@
|
||||
<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.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
|
||||
</Project>
|
@@ -4,7 +4,7 @@
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.ValueTuple" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
@@ -12,7 +12,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Security.Principal.Windows" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
@@ -28,27 +28,27 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.3.0.0" newVersion="8.3.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" />
|
||||
@@ -56,7 +56,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
@@ -1,27 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="Castle.Core" version="5.1.1" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.22.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.5.2" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.12.0" targetFramework="net48" />
|
||||
<package id="Castle.Core" version="5.2.1" targetFramework="net48" />
|
||||
<package id="Microsoft.ApplicationInsights" version="2.23.0" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.Telemetry" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.TrxReport.Abstractions" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Extensions.VSTestBridge" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.Testing.Platform.MSBuild" version="1.8.3" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.AdapterUtilities" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Microsoft.TestPlatform.ObjectModel" version="17.14.1" targetFramework="net48" />
|
||||
<package id="Moq" version="4.20.72" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.7.2" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.7.2" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.7.2" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.12.1" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.1" targetFramework="net48" />
|
||||
<package id="MSTest.Analyzers" version="3.10.3" targetFramework="net48" developmentDependency="true" />
|
||||
<package id="MSTest.TestAdapter" version="3.10.3" targetFramework="net48" />
|
||||
<package id="MSTest.TestFramework" version="3.10.3" targetFramework="net48" />
|
||||
<package id="NuGet.Frameworks" version="6.14.0" targetFramework="net48" />
|
||||
<package id="System.Buffers" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Collections.Immutable" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Diagnostics.DiagnosticSource" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.IO.FileSystem.Primitives" version="4.3.0" targetFramework="net48" />
|
||||
<package id="System.IO.Packaging" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.1" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.0" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.0" targetFramework="net48" />
|
||||
<package id="System.ValueTuple" version="4.5.0" targetFramework="net48" />
|
||||
<package id="System.IO.Packaging" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Memory" version="4.6.3" targetFramework="net48" />
|
||||
<package id="System.Numerics.Vectors" version="4.6.1" targetFramework="net48" />
|
||||
<package id="System.Reflection.Metadata" version="9.0.8" targetFramework="net48" />
|
||||
<package id="System.Runtime.CompilerServices.Unsafe" version="6.1.2" targetFramework="net48" />
|
||||
<package id="System.Threading.Tasks.Extensions" version="4.6.3" targetFramework="net48" />
|
||||
<package id="System.ValueTuple" version="4.6.1" targetFramework="net48" />
|
||||
</packages>
|
@@ -15,7 +15,7 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="KGySoft.CoreLibraries" publicKeyToken="b45eba277439ddfe" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-8.3.0.0" newVersion="8.3.0.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.0.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="KGySoft.Drawing.Core" publicKeyToken="b45eba277439ddfe" culture="neutral" />
|
||||
|
@@ -82,6 +82,11 @@ namespace SafeExamBrowser.Client
|
||||
/// </summary>
|
||||
internal IProctoringController Proctoring { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that the user already provided the correct quit password.
|
||||
/// </summary>
|
||||
internal bool QuitPasswordValidated { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The client responsibilities.
|
||||
/// </summary>
|
||||
|
@@ -58,6 +58,7 @@ using SafeExamBrowser.UserInterface.Contracts.FileSystemDialog;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
using SafeExamBrowser.UserInterface.Shared;
|
||||
using SafeExamBrowser.UserInterface.Shared.Activators;
|
||||
using SafeExamBrowser.WindowsApi;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
@@ -92,6 +93,7 @@ namespace SafeExamBrowser.Client
|
||||
private ITaskview taskview;
|
||||
private IUserInfo userInfo;
|
||||
private IText text;
|
||||
private WindowGuard windowGuard;
|
||||
private IUserInterfaceFactory uiFactory;
|
||||
|
||||
internal ClientController ClientController { get; private set; }
|
||||
@@ -105,6 +107,7 @@ namespace SafeExamBrowser.Client
|
||||
|
||||
var processFactory = new ProcessFactory(ModuleLogger(nameof(ProcessFactory)));
|
||||
|
||||
windowGuard = new WindowGuard(ModuleLogger(nameof(WindowGuard)));
|
||||
uiFactory = BuildUserInterfaceFactory();
|
||||
actionCenter = uiFactory.CreateActionCenter();
|
||||
context = new ClientContext();
|
||||
@@ -162,6 +165,7 @@ namespace SafeExamBrowser.Client
|
||||
operations.Enqueue(new I18nOperation(logger, text));
|
||||
operations.Enqueue(new RuntimeConnectionOperation(context, logger, runtimeProxy, authenticationToken));
|
||||
operations.Enqueue(new ConfigurationOperation(context, logger, runtimeProxy));
|
||||
operations.Enqueue(new WindowGuardOperation(context, logger, windowGuard));
|
||||
operations.Enqueue(new DelegateOperation(UpdateAppConfig));
|
||||
operations.Enqueue(new DelegateOperation(BuildIntegrityModule));
|
||||
operations.Enqueue(new DelegateOperation(BuildPowerSupply));
|
||||
@@ -169,6 +173,7 @@ namespace SafeExamBrowser.Client
|
||||
operations.Enqueue(new ClientHostDisconnectionOperation(context, logger, FIVE_SECONDS));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildKeyboardInterceptorOperation));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildMouseInterceptorOperation));
|
||||
operations.Enqueue(new PermissionOperation(context, logger, networkAdapter));
|
||||
operations.Enqueue(new ApplicationOperation(context, applicationFactory, fileSystemDialog, logger, messageBox, applicationMonitor, splashScreen, text));
|
||||
operations.Enqueue(new DisplayMonitorOperation(context, displayMonitor, logger, taskbar));
|
||||
operations.Enqueue(new LazyInitializationOperation(BuildShellOperation));
|
||||
@@ -196,7 +201,7 @@ namespace SafeExamBrowser.Client
|
||||
responsibilities.Enqueue(new IntegrityResponsibility(context, ModuleLogger(nameof(IntegrityResponsibility)), text));
|
||||
responsibilities.Enqueue(new MonitoringResponsibility(actionCenter, applicationMonitor, context, coordinator, displayMonitor, explorerShell, ModuleLogger(nameof(MonitoringResponsibility)), sentinel, taskbar, text));
|
||||
responsibilities.Enqueue(new NetworkResponsibility(context, ModuleLogger(nameof(NetworkResponsibility)), networkAdapter, text, uiFactory));
|
||||
responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), uiFactory));
|
||||
responsibilities.Enqueue(new ProctoringResponsibility(context, ModuleLogger(nameof(ProctoringResponsibility)), messageBox, uiFactory));
|
||||
responsibilities.Enqueue(new ServerResponsibility(context, coordinator, ModuleLogger(nameof(ServerResponsibility)), text));
|
||||
responsibilities.Enqueue(new ShellResponsibility(actionCenter, context, new HashAlgorithm(), ModuleLogger(nameof(ShellResponsibility)), messageBox, taskbar, uiFactory));
|
||||
|
||||
@@ -337,7 +342,8 @@ namespace SafeExamBrowser.Client
|
||||
{
|
||||
var keyGenerator = new KeyGenerator(context.AppConfig, context.IntegrityModule, ModuleLogger(nameof(KeyGenerator)));
|
||||
var server = new ServerProxy(context.AppConfig, keyGenerator, ModuleLogger(nameof(ServerProxy)), systemInfo, userInfo, powerSupply, networkAdapter);
|
||||
var operation = new ServerOperation(context, logger, server);
|
||||
var invigilator = new Invigilator(ModuleLogger(nameof(Invigilator)), server);
|
||||
var operation = new ServerOperation(actionCenter, context, invigilator, logger, server, taskbar, uiFactory);
|
||||
|
||||
context.Server = server;
|
||||
|
||||
@@ -381,9 +387,9 @@ namespace SafeExamBrowser.Client
|
||||
switch (uiMode)
|
||||
{
|
||||
case UserInterfaceMode.Mobile:
|
||||
return new Mobile.FileSystemDialogFactory(systemInfo, text);
|
||||
return new Mobile.FileSystemDialogFactory(systemInfo, text, windowGuard);
|
||||
default:
|
||||
return new Desktop.FileSystemDialogFactory(systemInfo, text);
|
||||
return new Desktop.FileSystemDialogFactory(systemInfo, text, windowGuard);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -403,9 +409,9 @@ namespace SafeExamBrowser.Client
|
||||
switch (uiMode)
|
||||
{
|
||||
case UserInterfaceMode.Mobile:
|
||||
return new Mobile.UserInterfaceFactory(text);
|
||||
return new Mobile.UserInterfaceFactory(text, windowGuard);
|
||||
default:
|
||||
return new Desktop.UserInterfaceFactory(text);
|
||||
return new Desktop.UserInterfaceFactory(text, windowGuard);
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -16,12 +16,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||
/// </summary>
|
||||
internal abstract class ClientOperation : IOperation
|
||||
{
|
||||
protected ClientContext Context { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// TODO: In case this event is neither used by the runtime, either remove it completely or then move it to a separate interface!
|
||||
/// </summary>
|
||||
public event ActionRequiredEventHandler ActionRequired { add { } remove { } }
|
||||
protected ClientContext Context { get; }
|
||||
|
||||
public abstract event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
|
64
SafeExamBrowser.Client/Operations/ClientOperationSequence.cs
Normal file
64
SafeExamBrowser.Client/Operations/ClientOperationSequence.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.Core.OperationModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ClientOperationSequence : OperationSequence<IOperation>
|
||||
{
|
||||
private readonly ISplashScreen splashScreen;
|
||||
|
||||
public ClientOperationSequence(ILogger logger, IEnumerable<IOperation> operations, ISplashScreen splashScreen) : base(logger, operations)
|
||||
{
|
||||
this.splashScreen = splashScreen;
|
||||
|
||||
ProgressChanged += Operations_ProgressChanged;
|
||||
StatusChanged += Operations_StatusChanged;
|
||||
}
|
||||
|
||||
private void Operations_ProgressChanged(ProgressChangedEventArgs args)
|
||||
{
|
||||
if (args.CurrentValue.HasValue)
|
||||
{
|
||||
splashScreen.SetValue(args.CurrentValue.Value);
|
||||
}
|
||||
|
||||
if (args.IsIndeterminate == true)
|
||||
{
|
||||
splashScreen.SetIndeterminate();
|
||||
}
|
||||
|
||||
if (args.MaxValue.HasValue)
|
||||
{
|
||||
splashScreen.SetMaxValue(args.MaxValue.Value);
|
||||
}
|
||||
|
||||
if (args.Progress == true)
|
||||
{
|
||||
splashScreen.Progress();
|
||||
}
|
||||
|
||||
if (args.Regress == true)
|
||||
{
|
||||
splashScreen.Regress();
|
||||
}
|
||||
}
|
||||
|
||||
private void Operations_StatusChanged(TextKey status)
|
||||
{
|
||||
splashScreen.UpdateStatus(status, true);
|
||||
}
|
||||
}
|
||||
}
|
60
SafeExamBrowser.Client/Operations/PermissionOperation.cs
Normal file
60
SafeExamBrowser.Client/Operations/PermissionOperation.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class PermissionOperation : ClientOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly INetworkAdapter networkAdapter;
|
||||
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public PermissionOperation(ClientContext context, ILogger logger, INetworkAdapter networkAdapter) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.networkAdapter = networkAdapter;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing permissions...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializePermissions);
|
||||
|
||||
RequestNetworkAdapterAccess();
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private void RequestNetworkAdapterAccess()
|
||||
{
|
||||
var granted = networkAdapter.RequestAccess();
|
||||
|
||||
if (granted)
|
||||
{
|
||||
logger.Info("Permission to access the wireless networking functionality has been granted.");
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Warn("Permission to access the wireless networking functionality has not been granted! " +
|
||||
"If required, please grant the location permission manually under 'Privacy & Security' in the system settings.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -11,7 +11,6 @@ using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Proctoring.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
@@ -51,12 +50,6 @@ namespace SafeExamBrowser.Client.Operations
|
||||
|
||||
controller.Initialize(Context.Settings.Proctoring);
|
||||
|
||||
if (Context.Settings.SessionMode == SessionMode.Server && Context.Settings.Proctoring.ShowRaiseHandNotification)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.ActionCenter, Context.Settings.Proctoring));
|
||||
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(controller, Location.Taskbar, Context.Settings.Proctoring));
|
||||
}
|
||||
|
||||
foreach (var notification in controller.Notifications)
|
||||
{
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateNotificationControl(notification, Location.ActionCenter));
|
||||
|
@@ -12,20 +12,37 @@ using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class ServerOperation : ClientOperation
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly IInvigilator invigilator;
|
||||
private readonly ILogger logger;
|
||||
private readonly IServerProxy server;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public ServerOperation(ClientContext context, ILogger logger, IServerProxy server) : base(context)
|
||||
public ServerOperation(
|
||||
IActionCenter actionCenter,
|
||||
ClientContext context,
|
||||
IInvigilator invigilator,
|
||||
ILogger logger,
|
||||
IServerProxy server,
|
||||
ITaskbar taskbar,
|
||||
IUserInterfaceFactory uiFactory) : base(context)
|
||||
{
|
||||
this.actionCenter = actionCenter;
|
||||
this.invigilator = invigilator;
|
||||
this.logger = logger;
|
||||
this.server = server;
|
||||
this.taskbar = taskbar;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override OperationResult Perform()
|
||||
@@ -42,6 +59,14 @@ namespace SafeExamBrowser.Client.Operations
|
||||
Context.AppConfig.ServerOauth2Token,
|
||||
Context.Settings.Server);
|
||||
server.StartConnectivity();
|
||||
|
||||
if (Context.Settings.Server.Invigilation.ShowRaiseHandNotification)
|
||||
{
|
||||
invigilator.Initialize(Context.Settings.Server.Invigilation);
|
||||
|
||||
actionCenter.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.ActionCenter, Context.Settings.Server.Invigilation));
|
||||
taskbar.AddNotificationControl(uiFactory.CreateRaiseHandControl(invigilator, Location.Taskbar, Context.Settings.Server.Invigilation));
|
||||
}
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
|
@@ -83,7 +83,7 @@ namespace SafeExamBrowser.Client.Operations
|
||||
InitializeSystemComponents();
|
||||
InitializeActionCenter();
|
||||
InitializeTaskbar();
|
||||
InitializeTaskview();
|
||||
//InitializeTaskview();
|
||||
InitializeActivators();
|
||||
InitializeAlwaysOnState();
|
||||
|
||||
@@ -112,11 +112,11 @@ namespace SafeExamBrowser.Client.Operations
|
||||
actionCenterActivator.Start();
|
||||
}
|
||||
|
||||
if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
|
||||
{
|
||||
taskview.Register(taskViewActivator);
|
||||
taskViewActivator.Start();
|
||||
}
|
||||
//if (Context.Settings.Keyboard.AllowAltTab && activator is ITaskviewActivator taskViewActivator)
|
||||
//{
|
||||
// taskview.Register(taskViewActivator);
|
||||
// taskViewActivator.Start();
|
||||
//}
|
||||
|
||||
if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator)
|
||||
{
|
||||
|
54
SafeExamBrowser.Client/Operations/WindowGuardOperation.cs
Normal file
54
SafeExamBrowser.Client/Operations/WindowGuardOperation.cs
Normal file
@@ -0,0 +1,54 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel;
|
||||
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Operations
|
||||
{
|
||||
internal class WindowGuardOperation : ClientOperation
|
||||
{
|
||||
private readonly ILogger logger;
|
||||
private readonly IWindowGuard guard;
|
||||
|
||||
public WindowGuardOperation(ClientContext context, ILogger logger, IWindowGuard guard) : base(context)
|
||||
{
|
||||
this.logger = logger;
|
||||
this.guard = guard;
|
||||
}
|
||||
|
||||
public override event StatusChangedEventHandler StatusChanged;
|
||||
|
||||
public override OperationResult Perform()
|
||||
{
|
||||
logger.Info("Initializing window guard...");
|
||||
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeWindowGuard);
|
||||
|
||||
if (Context.Settings.Proctoring.Enabled || Context.Settings.Security.AllowWindowCapture)
|
||||
{
|
||||
guard.Deactivate();
|
||||
logger.Info($"Deactivated window guard because {(Context.Settings.Proctoring.Enabled ? "proctoring" : "window capturing")} is enabled.");
|
||||
}
|
||||
else
|
||||
{
|
||||
guard.Activate();
|
||||
logger.Info("Activated window guard.");
|
||||
}
|
||||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
public override OperationResult Revert()
|
||||
{
|
||||
return OperationResult.Success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -51,6 +51,6 @@ using System.Windows;
|
||||
// You can specify all the values or you can default the Build and Revision Numbers
|
||||
// by using the '*' as shown below:
|
||||
// [assembly: AssemblyVersion("1.0.*")]
|
||||
[assembly: AssemblyVersion("3.9.0.787")]
|
||||
[assembly: AssemblyFileVersion("3.9.0.787")]
|
||||
[assembly: AssemblyVersion("3.10.0.826")]
|
||||
[assembly: AssemblyFileVersion("3.10.0.826")]
|
||||
[assembly: AssemblyInformationalVersion("1.0.0.0")]
|
||||
|
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class ApplicationsResponsibility : ClientResponsibility
|
||||
{
|
||||
public ApplicationsResponsibility(ClientContext context, ILogger logger) : base(context, logger)
|
||||
{
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
if (task == ClientTask.AutoStartApplications)
|
||||
{
|
||||
AutoStart();
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoStart()
|
||||
{
|
||||
foreach (var application in Context.Applications)
|
||||
{
|
||||
if (application.AutoStart)
|
||||
{
|
||||
Logger.Info($"Auto-starting '{application.Name}'...");
|
||||
application.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
212
SafeExamBrowser.Client/Responsibilities/BrowserResponsibility.cs
Normal file
212
SafeExamBrowser.Client/Responsibilities/BrowserResponsibility.cs
Normal file
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading;
|
||||
using SafeExamBrowser.Browser.Contracts;
|
||||
using SafeExamBrowser.Browser.Contracts.Events;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class BrowserResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly ICoordinator coordinator;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly IRuntimeProxy runtime;
|
||||
private readonly ISplashScreen splashScreen;
|
||||
private readonly ITaskbar taskbar;
|
||||
|
||||
private IBrowserApplication Browser => Context.Browser;
|
||||
|
||||
public BrowserResponsibility(
|
||||
ClientContext context,
|
||||
ICoordinator coordinator,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IRuntimeProxy runtime,
|
||||
ISplashScreen splashScreen,
|
||||
ITaskbar taskbar) : base(context, logger)
|
||||
{
|
||||
this.coordinator = coordinator;
|
||||
this.messageBox = messageBox;
|
||||
this.runtime = runtime;
|
||||
this.splashScreen = splashScreen;
|
||||
this.taskbar = taskbar;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.AutoStartApplications:
|
||||
AutoStartBrowser();
|
||||
break;
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void AutoStartBrowser()
|
||||
{
|
||||
if (Settings.Browser.EnableBrowser && Browser.AutoStart)
|
||||
{
|
||||
Logger.Info("Auto-starting browser...");
|
||||
Browser.Start();
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
if (Browser != default)
|
||||
{
|
||||
Browser.ConfigurationDownloadRequested -= Browser_ConfigurationDownloadRequested;
|
||||
Browser.LoseFocusRequested -= Browser_LoseFocusRequested;
|
||||
Browser.TerminationRequested -= Browser_TerminationRequested;
|
||||
Browser.UserIdentifierDetected -= Browser_UserIdentifierDetected;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
Browser.ConfigurationDownloadRequested += Browser_ConfigurationDownloadRequested;
|
||||
Browser.LoseFocusRequested += Browser_LoseFocusRequested;
|
||||
Browser.TerminationRequested += Browser_TerminationRequested;
|
||||
Browser.UserIdentifierDetected += Browser_UserIdentifierDetected;
|
||||
}
|
||||
|
||||
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);
|
||||
var hasUrl = !string.IsNullOrWhiteSpace(Settings.Security.ReconfigurationUrl);
|
||||
|
||||
if (hasQuitPassword)
|
||||
{
|
||||
if (hasUrl)
|
||||
{
|
||||
var expression = Regex.Escape(Settings.Security.ReconfigurationUrl).Replace(@"\*", ".*");
|
||||
var regex = new Regex($"^{expression}$", RegexOptions.IgnoreCase);
|
||||
var sebUrl = url.Replace(Uri.UriSchemeHttps, Context.AppConfig.SebUriSchemeSecure).Replace(Uri.UriSchemeHttp, Context.AppConfig.SebUriScheme);
|
||||
|
||||
allow = Settings.Security.AllowReconfiguration && (regex.IsMatch(url) || regex.IsMatch(sebUrl));
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("The active configuration does not contain a valid reconfiguration URL!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
allow = Settings.ConfigurationMode == ConfigurationMode.ConfigureClient || Settings.Security.AllowReconfiguration;
|
||||
}
|
||||
|
||||
return allow;
|
||||
}
|
||||
|
||||
private void Browser_ConfigurationDownloadFinished(bool success, string url, string filePath = null)
|
||||
{
|
||||
if (success)
|
||||
{
|
||||
PrepareShutdown();
|
||||
|
||||
var communication = runtime.RequestReconfiguration(filePath, url);
|
||||
|
||||
if (communication.Success)
|
||||
{
|
||||
Logger.Info($"Sent reconfiguration request for '{filePath}' to the runtime.");
|
||||
}
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_LoseFocusRequested(bool forward)
|
||||
{
|
||||
taskbar.Focus(forward);
|
||||
}
|
||||
|
||||
private void Browser_UserIdentifierDetected(string identifier)
|
||||
{
|
||||
if (Settings.SessionMode == SessionMode.Server)
|
||||
{
|
||||
var response = Context.Server.SendUserIdentifier(identifier);
|
||||
|
||||
while (!response.Success)
|
||||
{
|
||||
Logger.Error($"Failed to communicate user identifier with server! {response.Message}");
|
||||
Thread.Sleep(Settings.Server.RequestAttemptInterval);
|
||||
response = Context.Server.SendUserIdentifier(identifier);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_TerminationRequested()
|
||||
{
|
||||
Logger.Info("Attempting to shutdown as requested by the browser...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
}
|
||||
}
|
175
SafeExamBrowser.Client/Responsibilities/ClientResponsibility.cs
Normal file
175
SafeExamBrowser.Client/Responsibilities/ClientResponsibility.cs
Normal file
@@ -0,0 +1,175 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SafeExamBrowser.Core.Contracts.ResponsibilityModel;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal abstract class ClientResponsibility : IResponsibility<ClientTask>
|
||||
{
|
||||
protected ClientContext Context { get; private set; }
|
||||
protected ILogger Logger { get; private set; }
|
||||
|
||||
protected AppSettings Settings => Context.Settings;
|
||||
|
||||
internal ClientResponsibility(ClientContext context, ILogger logger)
|
||||
{
|
||||
Context = context;
|
||||
Logger = logger;
|
||||
}
|
||||
|
||||
public abstract void Assume(ClientTask task);
|
||||
|
||||
protected void PauseActivators()
|
||||
{
|
||||
foreach (var activator in Context.Activators)
|
||||
{
|
||||
activator.Pause();
|
||||
}
|
||||
}
|
||||
|
||||
protected bool IsValidQuitPassword(string password)
|
||||
{
|
||||
var actual = Context.HashAlgorithm.GenerateHashFor(password);
|
||||
var expected = Settings.Security.QuitPasswordHash;
|
||||
var valid = expected.Equals(actual, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (valid)
|
||||
{
|
||||
Context.QuitPasswordValidated = true;
|
||||
}
|
||||
|
||||
return valid;
|
||||
}
|
||||
|
||||
protected void PrepareShutdown()
|
||||
{
|
||||
Context.Responsibilities.Delegate(ClientTask.PrepareShutdown_Wave1);
|
||||
Context.Responsibilities.Delegate(ClientTask.PrepareShutdown_Wave2);
|
||||
}
|
||||
|
||||
protected void ResumeActivators()
|
||||
{
|
||||
foreach (var activator in Context.Activators)
|
||||
{
|
||||
activator.Resume();
|
||||
}
|
||||
}
|
||||
|
||||
protected LockScreenResult ShowLockScreen(string message, string title, IEnumerable<LockScreenOption> options)
|
||||
{
|
||||
Logger.Info("Showing lock screen...");
|
||||
|
||||
PauseActivators();
|
||||
Context.LockScreen = Context.UserInterfaceFactory.CreateLockScreen(message, title, options, Settings.UserInterface.LockScreen);
|
||||
Context.LockScreen.Show();
|
||||
|
||||
if (Settings.SessionMode == SessionMode.Server)
|
||||
{
|
||||
SendLockScreenNotification(message);
|
||||
}
|
||||
|
||||
var result = WaitForLockScreenResolution();
|
||||
|
||||
Context.LockScreen.Close();
|
||||
Context.LockScreen = default;
|
||||
ResumeActivators();
|
||||
|
||||
if (Settings.SessionMode == SessionMode.Server)
|
||||
{
|
||||
SendLockScreenConfirmation();
|
||||
}
|
||||
|
||||
Logger.Info("Closed lock screen.");
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
protected bool TryRequestShutdown()
|
||||
{
|
||||
PrepareShutdown();
|
||||
|
||||
var communication = Context.Runtime.RequestShutdown();
|
||||
|
||||
if (!communication.Success)
|
||||
{
|
||||
Logger.Error("Failed to communicate shutdown request to the runtime!");
|
||||
Context.MessageBox.Show(TextKey.MessageBox_QuitError, TextKey.MessageBox_QuitErrorTitle, icon: MessageBoxIcon.Error);
|
||||
}
|
||||
|
||||
return communication.Success;
|
||||
}
|
||||
|
||||
private void SendLockScreenConfirmation()
|
||||
{
|
||||
var response = Context.Server.ConfirmLockScreen();
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
Logger.Error($"Failed to send lock screen confirmation to server! Message: {response.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
private void SendLockScreenNotification(string message)
|
||||
{
|
||||
var response = Context.Server.LockScreen(message);
|
||||
|
||||
if (!response.Success)
|
||||
{
|
||||
Logger.Error($"Failed to send lock screen notification to server! Message: {response.Message}.");
|
||||
}
|
||||
}
|
||||
|
||||
private LockScreenResult WaitForLockScreenResolution()
|
||||
{
|
||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||
var result = default(LockScreenResult);
|
||||
|
||||
for (var unlocked = false; !unlocked;)
|
||||
{
|
||||
result = Context.LockScreen.WaitForResult();
|
||||
|
||||
if (result.Canceled)
|
||||
{
|
||||
Logger.Info("The lock screen has been canceled automatically.");
|
||||
unlocked = true;
|
||||
}
|
||||
else if (hasQuitPassword)
|
||||
{
|
||||
var passwordHash = Context.HashAlgorithm.GenerateHashFor(result.Password);
|
||||
var isCorrect = Settings.Security.QuitPasswordHash.Equals(passwordHash, StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (isCorrect)
|
||||
{
|
||||
Logger.Info("The user entered the correct unlock password.");
|
||||
unlocked = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("The user entered a wrong unlock password.");
|
||||
Context.MessageBox.Show(TextKey.MessageBox_InvalidUnlockPassword, TextKey.MessageBox_InvalidUnlockPasswordTitle, icon: MessageBoxIcon.Warning, parent: Context.LockScreen);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn($"No unlock password is defined, allowing user to resume session!");
|
||||
unlocked = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
73
SafeExamBrowser.Client/Responsibilities/ClientTask.cs
Normal file
73
SafeExamBrowser.Client/Responsibilities/ClientTask.cs
Normal file
@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Defines all tasks assumed by the responsibilities of the client application.
|
||||
/// </summary>
|
||||
internal enum ClientTask
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto-start the browser and any potential third-party applications.
|
||||
/// </summary>
|
||||
AutoStartApplications,
|
||||
|
||||
/// <summary>
|
||||
/// Close the shell.
|
||||
/// </summary>
|
||||
CloseShell,
|
||||
|
||||
/// <summary>
|
||||
/// Deregister all event handlers during application termination.
|
||||
/// </summary>
|
||||
DeregisterEvents,
|
||||
|
||||
/// <summary>
|
||||
/// Execute wave 1 of the application shutdown preparation. It should be used for potentially long-running operations which require all
|
||||
/// other (security) functionalities to continue working normally.
|
||||
/// </summary>
|
||||
PrepareShutdown_Wave1,
|
||||
|
||||
/// <summary>
|
||||
/// Execute wave 2 of the application shutdown preparation. It should be used by all remaining responsibilities which must continue to work
|
||||
/// normally during the execution of wave 1.
|
||||
/// </summary>
|
||||
PrepareShutdown_Wave2,
|
||||
|
||||
/// <summary>
|
||||
/// Register all event handlers during application initialization.
|
||||
/// </summary>
|
||||
RegisterEvents,
|
||||
|
||||
/// <summary>
|
||||
/// Schedule the verification of the application integrity.
|
||||
/// </summary>
|
||||
ScheduleIntegrityVerification,
|
||||
|
||||
/// <summary>
|
||||
/// Show the shell.
|
||||
/// </summary>
|
||||
ShowShell,
|
||||
|
||||
/// <summary>
|
||||
/// Start the monitoring of different (security) aspects.
|
||||
/// </summary>
|
||||
StartMonitoring,
|
||||
|
||||
/// <summary>
|
||||
/// Update the session integrity during application termination.
|
||||
/// </summary>
|
||||
UpdateSessionIntegrity,
|
||||
|
||||
/// <summary>
|
||||
/// Verify the session integrity during application initialization.
|
||||
/// </summary>
|
||||
VerifySessionIntegrity
|
||||
}
|
||||
}
|
@@ -0,0 +1,192 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.Communication.Contracts.Data;
|
||||
using SafeExamBrowser.Communication.Contracts.Events;
|
||||
using SafeExamBrowser.Communication.Contracts.Hosts;
|
||||
using SafeExamBrowser.Communication.Contracts.Proxies;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts.Data;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class CommunicationResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly ICoordinator coordinator;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly IRuntimeProxy runtime;
|
||||
private readonly Action shutdown;
|
||||
private readonly ISplashScreen splashScreen;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private IClientHost ClientHost => Context.ClientHost;
|
||||
|
||||
public CommunicationResponsibility(
|
||||
ClientContext context,
|
||||
ICoordinator coordinator,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IRuntimeProxy runtime,
|
||||
Action shutdown,
|
||||
ISplashScreen splashScreen,
|
||||
IText text,
|
||||
IUserInterfaceFactory uiFactory) : base(context, logger)
|
||||
{
|
||||
this.coordinator = coordinator;
|
||||
this.messageBox = messageBox;
|
||||
this.runtime = runtime;
|
||||
this.shutdown = shutdown;
|
||||
this.splashScreen = splashScreen;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
if (ClientHost != default)
|
||||
{
|
||||
ClientHost.ExamSelectionRequested -= ClientHost_ExamSelectionRequested;
|
||||
ClientHost.MessageBoxRequested -= ClientHost_MessageBoxRequested;
|
||||
ClientHost.PasswordRequested -= ClientHost_PasswordRequested;
|
||||
ClientHost.ReconfigurationAborted -= ClientHost_ReconfigurationAborted;
|
||||
ClientHost.ReconfigurationDenied -= ClientHost_ReconfigurationDenied;
|
||||
ClientHost.ServerFailureActionRequested -= ClientHost_ServerFailureActionRequested;
|
||||
ClientHost.Shutdown -= ClientHost_Shutdown;
|
||||
}
|
||||
|
||||
runtime.ConnectionLost -= Runtime_ConnectionLost;
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
ClientHost.ExamSelectionRequested += ClientHost_ExamSelectionRequested;
|
||||
ClientHost.MessageBoxRequested += ClientHost_MessageBoxRequested;
|
||||
ClientHost.PasswordRequested += ClientHost_PasswordRequested;
|
||||
ClientHost.ReconfigurationAborted += ClientHost_ReconfigurationAborted;
|
||||
ClientHost.ReconfigurationDenied += ClientHost_ReconfigurationDenied;
|
||||
ClientHost.ServerFailureActionRequested += ClientHost_ServerFailureActionRequested;
|
||||
ClientHost.Shutdown += ClientHost_Shutdown;
|
||||
|
||||
runtime.ConnectionLost += Runtime_ConnectionLost;
|
||||
}
|
||||
|
||||
private void ClientHost_ExamSelectionRequested(ExamSelectionRequestEventArgs args)
|
||||
{
|
||||
Logger.Info($"Received exam selection request with id '{args.RequestId}'.");
|
||||
|
||||
var exams = args.Exams.Select(e => new Exam { Id = e.id, LmsName = e.lms, Name = e.name, Url = e.url });
|
||||
var dialog = uiFactory.CreateExamSelectionDialog(exams);
|
||||
var result = dialog.Show();
|
||||
|
||||
runtime.SubmitExamSelectionResult(args.RequestId, result.Success, result.SelectedExam?.Id);
|
||||
Logger.Info($"Exam selection request with id '{args.RequestId}' is complete.");
|
||||
}
|
||||
|
||||
private void ClientHost_MessageBoxRequested(MessageBoxRequestEventArgs args)
|
||||
{
|
||||
Logger.Info($"Received message box request with id '{args.RequestId}'.");
|
||||
|
||||
var action = (MessageBoxAction) args.Action;
|
||||
var icon = (MessageBoxIcon) args.Icon;
|
||||
var result = messageBox.Show(args.Message, args.Title, action, icon, parent: splashScreen);
|
||||
|
||||
runtime.SubmitMessageBoxResult(args.RequestId, (int) result);
|
||||
Logger.Info($"Message box request with id '{args.RequestId}' yielded result '{result}'.");
|
||||
}
|
||||
|
||||
private void ClientHost_PasswordRequested(PasswordRequestEventArgs args)
|
||||
{
|
||||
var message = default(TextKey);
|
||||
var title = default(TextKey);
|
||||
|
||||
Logger.Info($"Received input request with id '{args.RequestId}' for the {args.Purpose.ToString().ToLower()} password.");
|
||||
|
||||
switch (args.Purpose)
|
||||
{
|
||||
case PasswordRequestPurpose.LocalAdministrator:
|
||||
message = TextKey.PasswordDialog_LocalAdminPasswordRequired;
|
||||
title = TextKey.PasswordDialog_LocalAdminPasswordRequiredTitle;
|
||||
break;
|
||||
case PasswordRequestPurpose.LocalSettings:
|
||||
message = TextKey.PasswordDialog_LocalSettingsPasswordRequired;
|
||||
title = TextKey.PasswordDialog_LocalSettingsPasswordRequiredTitle;
|
||||
break;
|
||||
case PasswordRequestPurpose.Settings:
|
||||
message = TextKey.PasswordDialog_SettingsPasswordRequired;
|
||||
title = TextKey.PasswordDialog_SettingsPasswordRequiredTitle;
|
||||
break;
|
||||
}
|
||||
|
||||
var dialog = uiFactory.CreatePasswordDialog(text.Get(message), text.Get(title));
|
||||
var result = dialog.Show();
|
||||
|
||||
runtime.SubmitPassword(args.RequestId, result.Success, result.Password);
|
||||
Logger.Info($"Password request with id '{args.RequestId}' was {(result.Success ? "successful" : "aborted by the user")}.");
|
||||
}
|
||||
|
||||
private void ClientHost_ReconfigurationAborted()
|
||||
{
|
||||
Logger.Info("The reconfiguration was aborted by the runtime.");
|
||||
splashScreen.Hide();
|
||||
coordinator.ReleaseReconfigurationLock();
|
||||
}
|
||||
|
||||
private void ClientHost_ReconfigurationDenied(ReconfigurationEventArgs args)
|
||||
{
|
||||
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)
|
||||
{
|
||||
Logger.Info($"Received server failure action request with id '{args.RequestId}'.");
|
||||
|
||||
var dialog = uiFactory.CreateServerFailureDialog(args.Message, args.ShowFallback);
|
||||
var result = dialog.Show();
|
||||
|
||||
runtime.SubmitServerFailureActionResult(args.RequestId, result.Abort, result.Fallback, result.Retry);
|
||||
Logger.Info($"Server failure action request with id '{args.RequestId}' is complete, the user chose to {(result.Abort ? "abort" : (result.Fallback ? "fallback" : "retry"))}.");
|
||||
}
|
||||
|
||||
private void ClientHost_Shutdown()
|
||||
{
|
||||
shutdown.Invoke();
|
||||
}
|
||||
|
||||
private void Runtime_ConnectionLost()
|
||||
{
|
||||
Logger.Error("Lost connection to the runtime!");
|
||||
|
||||
messageBox.Show(TextKey.MessageBox_ApplicationError, TextKey.MessageBox_ApplicationErrorTitle, icon: MessageBoxIcon.Error);
|
||||
shutdown.Invoke();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using SafeExamBrowser.Configuration.Contracts.Integrity;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class IntegrityResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly IText text;
|
||||
|
||||
private IIntegrityModule IntegrityModule => Context.IntegrityModule;
|
||||
|
||||
public IntegrityResponsibility(ClientContext context, ILogger logger, IText text) : base(context, logger)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.ScheduleIntegrityVerification:
|
||||
ScheduleIntegrityVerification();
|
||||
break;
|
||||
case ClientTask.UpdateSessionIntegrity:
|
||||
UpdateSessionIntegrity();
|
||||
break;
|
||||
case ClientTask.VerifySessionIntegrity:
|
||||
VerifySessionIntegrity();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void ScheduleIntegrityVerification()
|
||||
{
|
||||
const int FIVE_MINUTES = 300000;
|
||||
const int TEN_MINUTES = 600000;
|
||||
|
||||
var timer = new System.Timers.Timer();
|
||||
|
||||
timer.AutoReset = false;
|
||||
timer.Elapsed += (o, args) => VerifyApplicationIntegrity();
|
||||
timer.Interval = TEN_MINUTES + (new Random().NextDouble() * FIVE_MINUTES);
|
||||
timer.Start();
|
||||
}
|
||||
|
||||
private void UpdateSessionIntegrity()
|
||||
{
|
||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings?.Security.QuitPasswordHash);
|
||||
|
||||
if (hasQuitPassword)
|
||||
{
|
||||
IntegrityModule?.ClearSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyApplicationIntegrity()
|
||||
{
|
||||
Logger.Info($"Attempting to verify application integrity...");
|
||||
|
||||
if (IntegrityModule.TryVerifyCodeSignature(out var isValid))
|
||||
{
|
||||
if (isValid)
|
||||
{
|
||||
Logger.Info("Application integrity successfully verified.");
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Application integrity is compromised!");
|
||||
ShowLockScreen(text.Get(TextKey.LockScreen_ApplicationIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Failed to verify application integrity!");
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifySessionIntegrity()
|
||||
{
|
||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||
|
||||
if (hasQuitPassword && Settings.Security.VerifySessionIntegrity)
|
||||
{
|
||||
Logger.Info($"Attempting to verify session integrity...");
|
||||
|
||||
if (IntegrityModule.TryVerifySessionIntegrity(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl, out var isValid))
|
||||
{
|
||||
if (isValid)
|
||||
{
|
||||
Logger.Info("Session integrity successfully verified.");
|
||||
IntegrityModule.CacheSession(Settings.Browser.ConfigurationKey, Settings.Browser.StartUrl);
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Session integrity is compromised!");
|
||||
Task.Delay(1000).ContinueWith(_ =>
|
||||
{
|
||||
ShowLockScreen(text.Get(TextKey.LockScreen_SessionIntegrityMessage), text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||
});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Warn("Failed to verify session integrity!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,334 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Win32;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
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.UserInterface.Contracts.Shell;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
using SafeExamBrowser.WindowsApi.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class MonitoringResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly IApplicationMonitor applicationMonitor;
|
||||
private readonly ICoordinator coordinator;
|
||||
private readonly IDisplayMonitor displayMonitor;
|
||||
private readonly IExplorerShell explorerShell;
|
||||
private readonly ISystemSentinel sentinel;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly IText text;
|
||||
|
||||
public MonitoringResponsibility(
|
||||
IActionCenter actionCenter,
|
||||
IApplicationMonitor applicationMonitor,
|
||||
ClientContext context,
|
||||
ICoordinator coordinator,
|
||||
IDisplayMonitor displayMonitor,
|
||||
IExplorerShell explorerShell,
|
||||
ILogger logger,
|
||||
ISystemSentinel sentinel,
|
||||
ITaskbar taskbar,
|
||||
IText text) : base(context, logger)
|
||||
{
|
||||
this.actionCenter = actionCenter;
|
||||
this.applicationMonitor = applicationMonitor;
|
||||
this.coordinator = coordinator;
|
||||
this.displayMonitor = displayMonitor;
|
||||
this.explorerShell = explorerShell;
|
||||
this.sentinel = sentinel;
|
||||
this.taskbar = taskbar;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.PrepareShutdown_Wave2:
|
||||
StopMonitoring();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
case ClientTask.StartMonitoring:
|
||||
StartMonitoring();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
applicationMonitor.ExplorerStarted -= ApplicationMonitor_ExplorerStarted;
|
||||
applicationMonitor.TerminationFailed -= ApplicationMonitor_TerminationFailed;
|
||||
displayMonitor.DisplayChanged -= DisplayMonitor_DisplaySettingsChanged;
|
||||
sentinel.CursorChanged -= Sentinel_CursorChanged;
|
||||
sentinel.EaseOfAccessChanged -= Sentinel_EaseOfAccessChanged;
|
||||
sentinel.SessionChanged -= Sentinel_SessionChanged;
|
||||
sentinel.StickyKeysChanged -= Sentinel_StickyKeysChanged;
|
||||
}
|
||||
|
||||
private void StopMonitoring()
|
||||
{
|
||||
sentinel.StopMonitoring();
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
applicationMonitor.ExplorerStarted += ApplicationMonitor_ExplorerStarted;
|
||||
applicationMonitor.TerminationFailed += ApplicationMonitor_TerminationFailed;
|
||||
displayMonitor.DisplayChanged += DisplayMonitor_DisplaySettingsChanged;
|
||||
sentinel.CursorChanged += Sentinel_CursorChanged;
|
||||
sentinel.EaseOfAccessChanged += Sentinel_EaseOfAccessChanged;
|
||||
sentinel.SessionChanged += Sentinel_SessionChanged;
|
||||
sentinel.StickyKeysChanged += Sentinel_StickyKeysChanged;
|
||||
}
|
||||
|
||||
private void StartMonitoring()
|
||||
{
|
||||
sentinel.StartMonitoringSystemEvents();
|
||||
|
||||
if (!Settings.Security.AllowStickyKeys)
|
||||
{
|
||||
sentinel.StartMonitoringStickyKeys();
|
||||
}
|
||||
|
||||
if (Settings.Security.VerifyCursorConfiguration)
|
||||
{
|
||||
sentinel.StartMonitoringCursors();
|
||||
}
|
||||
|
||||
if (Settings.Service.IgnoreService)
|
||||
{
|
||||
sentinel.StartMonitoringEaseOfAccess();
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplicationMonitor_ExplorerStarted()
|
||||
{
|
||||
Logger.Info("Trying to terminate Windows explorer...");
|
||||
explorerShell.Terminate();
|
||||
|
||||
Logger.Info("Re-initializing working area...");
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
|
||||
Logger.Info("Re-initializing shell...");
|
||||
actionCenter.InitializeBounds();
|
||||
taskbar.InitializeBounds();
|
||||
|
||||
Logger.Info("Desktop successfully restored.");
|
||||
}
|
||||
|
||||
private void ApplicationMonitor_TerminationFailed(IEnumerable<RunningApplication> applications)
|
||||
{
|
||||
var applicationList = string.Join(Environment.NewLine, applications.Select(a => $"- {a.Name}"));
|
||||
var message = $"{text.Get(TextKey.LockScreen_ApplicationsMessage)}{Environment.NewLine}{Environment.NewLine}{applicationList}";
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var allowOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsAllowOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_ApplicationsTerminateOption) };
|
||||
|
||||
Logger.Warn("Detected termination failure of blacklisted application(s)!");
|
||||
|
||||
var result = ShowLockScreen(message, title, new[] { allowOption, terminateOption });
|
||||
|
||||
if (result.OptionId == allowOption.Id)
|
||||
{
|
||||
Logger.Info($"The blacklisted application(s) {string.Join(", ", applications.Select(a => $"'{a.Name}'"))} will be temporarily allowed.");
|
||||
}
|
||||
else if (result.OptionId == terminateOption.Id)
|
||||
{
|
||||
Logger.Info("Attempting to shutdown as requested by the user...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
private void DisplayMonitor_DisplaySettingsChanged()
|
||||
{
|
||||
Logger.Info("Re-initializing working area...");
|
||||
displayMonitor.InitializePrimaryDisplay(Settings.UserInterface.Taskbar.EnableTaskbar ? taskbar.GetAbsoluteHeight() : 0);
|
||||
|
||||
Logger.Info("Re-initializing shell...");
|
||||
actionCenter.InitializeBounds();
|
||||
Context.LockScreen?.InitializeBounds();
|
||||
taskbar.InitializeBounds();
|
||||
|
||||
Logger.Info("Desktop successfully restored.");
|
||||
|
||||
if (!displayMonitor.ValidateConfiguration(Settings.Display).IsAllowed)
|
||||
{
|
||||
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationContinueOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_DisplayConfigurationTerminateOption) };
|
||||
var message = text.Get(TextKey.LockScreen_DisplayConfigurationMessage);
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var result = ShowLockScreen(message, title, new[] { continueOption, terminateOption });
|
||||
|
||||
if (result.OptionId == terminateOption.Id)
|
||||
{
|
||||
Logger.Info("Attempting to shutdown as requested by the user...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void Sentinel_CursorChanged(SentinelEventArgs args)
|
||||
{
|
||||
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) };
|
||||
|
||||
args.Allow = true;
|
||||
Logger.Info("Cursor 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("Cursor changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Sentinel_EaseOfAccessChanged(SentinelEventArgs args)
|
||||
{
|
||||
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) };
|
||||
|
||||
args.Allow = true;
|
||||
Logger.Info("Ease of access 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("Ease of access changed but lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Sentinel_SessionChanged(SessionSwitchReason reason)
|
||||
{
|
||||
|
||||
var allowed = !Settings.Service.IgnoreService && (!Settings.Service.DisableUserLock || !Settings.Service.DisableUserSwitch);
|
||||
var disabled = Settings.Security.DisableSessionChangeLockScreen;
|
||||
var ignore = Settings.Service.IgnoreService && (reason == SessionSwitchReason.SessionLock || reason == SessionSwitchReason.SessionUnlock);
|
||||
|
||||
if (allowed || disabled)
|
||||
{
|
||||
Logger.Info($"Detected user session change ({reason}), but {(allowed ? "session locking and/or switching is allowed" : "lock screen is disabled")}.");
|
||||
}
|
||||
else if (ignore)
|
||||
{
|
||||
Logger.Info($"Ignoring user session change ({reason}).");
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = text.Get(Settings.Service.IgnoreService ? TextKey.LockScreen_UserSwitchMessage : TextKey.LockScreen_UserSessionMessage);
|
||||
var title = text.Get(TextKey.LockScreen_Title);
|
||||
var continueOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionContinueOption) };
|
||||
var terminateOption = new LockScreenOption { Text = text.Get(TextKey.LockScreen_UserSessionTerminateOption) };
|
||||
|
||||
Logger.Warn($"User session changed ({reason})! 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.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network;
|
||||
using SafeExamBrowser.SystemComponents.Contracts.Network.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class NetworkResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly INetworkAdapter networkAdapter;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public NetworkResponsibility(ClientContext context, ILogger logger, INetworkAdapter networkAdapter, IText text, IUserInterfaceFactory uiFactory) : base(context, logger)
|
||||
{
|
||||
this.networkAdapter = networkAdapter;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
networkAdapter.CredentialsRequired -= NetworkAdapter_CredentialsRequired;
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
networkAdapter.CredentialsRequired += NetworkAdapter_CredentialsRequired;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Threading.Tasks;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Proctoring.Contracts;
|
||||
using SafeExamBrowser.Proctoring.Contracts.Events;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Proctoring.Events;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class ProctoringResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private bool cancel;
|
||||
|
||||
private IProctoringController Proctoring => Context.Proctoring;
|
||||
|
||||
public ProctoringResponsibility(
|
||||
ClientContext context,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
IUserInterfaceFactory uiFactory) : base(context, logger)
|
||||
{
|
||||
this.messageBox = messageBox;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
if (task == ClientTask.PrepareShutdown_Wave1)
|
||||
{
|
||||
FinalizeProctoring();
|
||||
}
|
||||
}
|
||||
|
||||
private void FinalizeProctoring()
|
||||
{
|
||||
if (Proctoring != default && Proctoring.HasRemainingWork())
|
||||
{
|
||||
var dialog = uiFactory.CreateProctoringFinalizationDialog(!Context.QuitPasswordValidated);
|
||||
var handler = new RemainingWorkUpdatedEventHandler((args) => Proctoring_RemainingWorkUpdated(dialog, args));
|
||||
|
||||
dialog.CancellationRequested += new CancellationRequestedEventHandler(() => Dialog_CancellationRequested(dialog));
|
||||
|
||||
Task.Run(() =>
|
||||
{
|
||||
Proctoring.RemainingWorkUpdated += handler;
|
||||
Proctoring.ExecuteRemainingWork();
|
||||
Proctoring.RemainingWorkUpdated -= handler;
|
||||
});
|
||||
|
||||
dialog.Show();
|
||||
}
|
||||
}
|
||||
|
||||
private void Dialog_CancellationRequested(IProctoringFinalizationDialog dialog)
|
||||
{
|
||||
var alreadyValidated = Context.QuitPasswordValidated;
|
||||
|
||||
if (alreadyValidated || IsValidQuitPassword(dialog.QuitPassword))
|
||||
{
|
||||
cancel = true;
|
||||
Logger.Info($"The user {(alreadyValidated ? "already " : "")}entered the correct quit password, cancelling remaining work...");
|
||||
}
|
||||
else
|
||||
{
|
||||
cancel = false;
|
||||
Logger.Info("The user entered the wrong quit password, remaining work will continue.");
|
||||
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning, parent: dialog);
|
||||
}
|
||||
}
|
||||
|
||||
private void Proctoring_RemainingWorkUpdated(IProctoringFinalizationDialog dialog, RemainingWorkUpdatedEventArgs args)
|
||||
{
|
||||
dialog.Update(args);
|
||||
args.CancellationRequested = cancel;
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Client.Contracts;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Server.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Windows.Data;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class ServerResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly ICoordinator coordinator;
|
||||
private readonly IText text;
|
||||
|
||||
private IServerProxy Server => Context.Server;
|
||||
|
||||
public ServerResponsibility(ClientContext context, ICoordinator coordinator, ILogger logger, IText text) : base(context, logger)
|
||||
{
|
||||
this.coordinator = coordinator;
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
if (Server != default)
|
||||
{
|
||||
Server.LockScreenConfirmed -= Server_LockScreenConfirmed;
|
||||
Server.LockScreenRequested -= Server_LockScreenRequested;
|
||||
Server.TerminationRequested -= Server_TerminationRequested;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
Server.LockScreenConfirmed += Server_LockScreenConfirmed;
|
||||
Server.LockScreenRequested += Server_LockScreenRequested;
|
||||
Server.TerminationRequested += Server_TerminationRequested;
|
||||
}
|
||||
|
||||
private void Server_LockScreenConfirmed()
|
||||
{
|
||||
Logger.Info("Closing lock screen as requested by the server...");
|
||||
Context.LockScreen?.Cancel();
|
||||
}
|
||||
|
||||
private void Server_LockScreenRequested(string message)
|
||||
{
|
||||
Logger.Info("Attempting to show lock screen as requested by the server...");
|
||||
|
||||
if (coordinator.RequestSessionLock())
|
||||
{
|
||||
ShowLockScreen(message, text.Get(TextKey.LockScreen_Title), Enumerable.Empty<LockScreenOption>());
|
||||
coordinator.ReleaseSessionLock();
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.Info("Lock screen is already active.");
|
||||
}
|
||||
}
|
||||
|
||||
private void Server_TerminationRequested()
|
||||
{
|
||||
Logger.Info("Attempting to shutdown as requested by the server...");
|
||||
TryRequestShutdown();
|
||||
}
|
||||
}
|
||||
}
|
179
SafeExamBrowser.Client/Responsibilities/ShellResponsibility.cs
Normal file
179
SafeExamBrowser.Client/Responsibilities/ShellResponsibility.cs
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Copyright (c) 2025 ETH Zürich, IT Services
|
||||
*
|
||||
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
using System.ComponentModel;
|
||||
using System.Linq;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts;
|
||||
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
||||
using SafeExamBrowser.UserInterface.Contracts.Shell;
|
||||
|
||||
namespace SafeExamBrowser.Client.Responsibilities
|
||||
{
|
||||
internal class ShellResponsibility : ClientResponsibility
|
||||
{
|
||||
private readonly IActionCenter actionCenter;
|
||||
private readonly IHashAlgorithm hashAlgorithm;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly ITaskbar taskbar;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public ShellResponsibility(
|
||||
IActionCenter actionCenter,
|
||||
ClientContext context,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
ILogger logger,
|
||||
IMessageBox messageBox,
|
||||
ITaskbar taskbar,
|
||||
IUserInterfaceFactory uiFactory) : base(context, logger)
|
||||
{
|
||||
this.actionCenter = actionCenter;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.messageBox = messageBox;
|
||||
this.taskbar = taskbar;
|
||||
this.uiFactory = uiFactory;
|
||||
}
|
||||
|
||||
public override void Assume(ClientTask task)
|
||||
{
|
||||
switch (task)
|
||||
{
|
||||
case ClientTask.CloseShell:
|
||||
CloseShell();
|
||||
break;
|
||||
case ClientTask.DeregisterEvents:
|
||||
DeregisterEvents();
|
||||
break;
|
||||
case ClientTask.RegisterEvents:
|
||||
RegisterEvents();
|
||||
break;
|
||||
case ClientTask.ShowShell:
|
||||
ShowShell();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void DeregisterEvents()
|
||||
{
|
||||
actionCenter.QuitButtonClicked -= Shell_QuitButtonClicked;
|
||||
taskbar.LoseFocusRequested -= Taskbar_LoseFocusRequested;
|
||||
taskbar.QuitButtonClicked -= Shell_QuitButtonClicked;
|
||||
|
||||
foreach (var activator in Context.Activators.OfType<ITerminationActivator>())
|
||||
{
|
||||
activator.Activated -= TerminationActivator_Activated;
|
||||
}
|
||||
}
|
||||
|
||||
private void RegisterEvents()
|
||||
{
|
||||
actionCenter.QuitButtonClicked += Shell_QuitButtonClicked;
|
||||
taskbar.LoseFocusRequested += Taskbar_LoseFocusRequested;
|
||||
taskbar.QuitButtonClicked += Shell_QuitButtonClicked;
|
||||
|
||||
foreach (var activator in Context.Activators.OfType<ITerminationActivator>())
|
||||
{
|
||||
activator.Activated += TerminationActivator_Activated;
|
||||
}
|
||||
}
|
||||
|
||||
private void CloseShell()
|
||||
{
|
||||
if (Settings?.UserInterface.ActionCenter.EnableActionCenter == true)
|
||||
{
|
||||
actionCenter.Close();
|
||||
}
|
||||
|
||||
if (Settings?.UserInterface.Taskbar.EnableTaskbar == true)
|
||||
{
|
||||
taskbar.Close();
|
||||
}
|
||||
}
|
||||
|
||||
private void ShowShell()
|
||||
{
|
||||
if (Settings.UserInterface.ActionCenter.EnableActionCenter)
|
||||
{
|
||||
actionCenter.Promote();
|
||||
}
|
||||
|
||||
if (Settings.UserInterface.Taskbar.EnableTaskbar)
|
||||
{
|
||||
taskbar.Show();
|
||||
}
|
||||
}
|
||||
|
||||
private void Shell_QuitButtonClicked(CancelEventArgs args)
|
||||
{
|
||||
PauseActivators();
|
||||
args.Cancel = !TryInitiateShutdown();
|
||||
ResumeActivators();
|
||||
}
|
||||
|
||||
private void Taskbar_LoseFocusRequested(bool forward)
|
||||
{
|
||||
Context.Browser.Focus(forward);
|
||||
}
|
||||
|
||||
private void TerminationActivator_Activated()
|
||||
{
|
||||
PauseActivators();
|
||||
TryInitiateShutdown();
|
||||
ResumeActivators();
|
||||
}
|
||||
|
||||
private bool TryInitiateShutdown()
|
||||
{
|
||||
var hasQuitPassword = !string.IsNullOrEmpty(Settings.Security.QuitPasswordHash);
|
||||
var initiateShutdown = hasQuitPassword ? TryValidateQuitPassword() : TryConfirmShutdown();
|
||||
var success = false;
|
||||
|
||||
if (initiateShutdown)
|
||||
{
|
||||
success = TryRequestShutdown();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
private bool TryConfirmShutdown()
|
||||
{
|
||||
var result = messageBox.Show(TextKey.MessageBox_Quit, TextKey.MessageBox_QuitTitle, MessageBoxAction.YesNo, MessageBoxIcon.Question);
|
||||
var quit = result == MessageBoxResult.Yes;
|
||||
|
||||
if (quit)
|
||||
{
|
||||
Logger.Info("The user chose to terminate the application.");
|
||||
}
|
||||
|
||||
return quit;
|
||||
}
|
||||
|
||||
private bool TryValidateQuitPassword()
|
||||
{
|
||||
var dialog = uiFactory.CreatePasswordDialog(TextKey.PasswordDialog_QuitPasswordRequired, TextKey.PasswordDialog_QuitPasswordRequiredTitle);
|
||||
var result = dialog.Show();
|
||||
var success = false;
|
||||
|
||||
if (result.Success && IsValidQuitPassword(result.Password))
|
||||
{
|
||||
success = true;
|
||||
Logger.Info("The user entered the correct quit password, the application will now terminate.");
|
||||
}
|
||||
else if (result.Success)
|
||||
{
|
||||
Logger.Info("The user entered the wrong quit password.");
|
||||
messageBox.Show(TextKey.MessageBox_InvalidQuitPassword, TextKey.MessageBox_InvalidQuitPasswordTitle, icon: MessageBoxIcon.Warning);
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
}
|
||||
}
|
@@ -80,6 +80,7 @@
|
||||
<Compile Include="Operations\ClientOperation.cs" />
|
||||
<Compile Include="Operations\ClientOperationSequence.cs" />
|
||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||
<Compile Include="Operations\PermissionOperation.cs" />
|
||||
<Compile Include="Operations\ProctoringOperation.cs" />
|
||||
<Compile Include="Operations\RuntimeConnectionOperation.cs" />
|
||||
<Compile Include="Communication\ClientHost.cs" />
|
||||
@@ -94,6 +95,7 @@
|
||||
<Compile Include="Operations\ApplicationOperation.cs" />
|
||||
<Compile Include="Operations\ServerOperation.cs" />
|
||||
<Compile Include="Operations\ShellOperation.cs" />
|
||||
<Compile Include="Operations\WindowGuardOperation.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs">
|
||||
<SubType>Code</SubType>
|
||||
</Compile>
|
||||
|
@@ -53,12 +53,11 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustCorrectlyHandleStartupException()
|
||||
{
|
||||
hostObject.Setup(h => h.Open()).Throws<Exception>();
|
||||
|
||||
sut.Start();
|
||||
Assert.ThrowsExactly<CommunicationException>(() => sut.Start());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
@@ -71,13 +70,12 @@ namespace SafeExamBrowser.Communication.UnitTests.Hosts
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(CommunicationException))]
|
||||
public void MustCorrectlyHandleShutdownException()
|
||||
{
|
||||
hostObject.Setup(h => h.Close()).Throws<Exception>();
|
||||
|
||||
sut.Start();
|
||||
sut.Stop();
|
||||
|
||||
Assert.ThrowsExactly<CommunicationException>(() => sut.Stop());
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@@ -171,14 +171,12 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustFailToSendIfNotConnected()
|
||||
{
|
||||
sut.Send(new Mock<Message>().Object);
|
||||
Assert.ThrowsExactly<InvalidOperationException>(() => sut.Send(new Mock<Message>().Object));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(InvalidOperationException))]
|
||||
public void MustFailToSendIfChannelNotOpen()
|
||||
{
|
||||
var proxy = new Mock<IProxyObject>();
|
||||
@@ -195,14 +193,14 @@ namespace SafeExamBrowser.Communication.UnitTests.Proxies
|
||||
var token = Guid.NewGuid();
|
||||
|
||||
sut.Connect(token);
|
||||
sut.Send(new Mock<Message>().Object);
|
||||
|
||||
Assert.ThrowsExactly<InvalidOperationException>(() => sut.Send(new Mock<Message>().Object));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
[ExpectedException(typeof(ArgumentNullException))]
|
||||
public void MustNotAllowSendingNull()
|
||||
{
|
||||
sut.Send(null);
|
||||
Assert.ThrowsExactly<ArgumentNullException>(() => sut.Send(null));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@@ -1,9 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="15.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props" Condition="Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" />
|
||||
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
|
||||
<PropertyGroup>
|
||||
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
|
||||
@@ -63,82 +63,83 @@
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="Castle.Core, Version=5.0.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Castle.Core.5.1.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
<HintPath>..\packages\Castle.Core.5.2.1\lib\net462\Castle.Core.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.22.0.997, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.22.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
<Reference Include="Microsoft.ApplicationInsights, Version=2.23.0.29, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.ApplicationInsights.2.23.0\lib\net46\Microsoft.ApplicationInsights.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.MSBuild, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.MSBuild.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.Telemetry, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.TrxReport.Abstractions, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.TrxReport.Abstractions.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.TrxReport.Abstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.5.2\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Extensions.VSTestBridge, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Extensions.VSTestBridge.1.8.3\lib\netstandard2.0\Microsoft.Testing.Extensions.VSTestBridge.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.5.2.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.5.2\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
<Reference Include="Microsoft.Testing.Platform, Version=1.8.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.Testing.Platform.1.8.3\lib\netstandard2.0\Microsoft.Testing.Platform.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.AdapterUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.AdapterUtilities.17.14.1\lib\net462\Microsoft.TestPlatform.AdapterUtilities.dll</HintPath>
|
||||
<Private>True</Private>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.CoreUtilities, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.CoreUtilities.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.TestPlatform.PlatformAbstractions, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.TestPlatform.PlatformAbstractions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.ObjectModel, Version=15.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.12.0\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
<HintPath>..\packages\Microsoft.TestPlatform.ObjectModel.17.14.1\lib\net462\Microsoft.VisualStudio.TestPlatform.ObjectModel.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions, Version=14.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.7.2\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
<HintPath>..\packages\MSTest.TestFramework.3.10.3\lib\net462\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="Moq, Version=4.20.72.0, Culture=neutral, PublicKeyToken=69f491c39445e920, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\Moq.4.20.72\lib\net462\Moq.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.12.1.1, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.12.1\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
<Reference Include="NuGet.Frameworks, Version=6.14.0.116, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\NuGet.Frameworks.6.14.0\lib\net472\NuGet.Frameworks.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Buffers, Version=4.0.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.0\lib\net462\System.Buffers.dll</HintPath>
|
||||
<Reference Include="System.Buffers, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Buffers.4.6.1\lib\net462\System.Buffers.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.1\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
<Reference Include="System.Collections.Immutable, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Collections.Immutable.9.0.8\lib\net462\System.Collections.Immutable.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Configuration" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.1\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
<Reference Include="System.Diagnostics.DiagnosticSource, Version=9.0.0.8, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Diagnostics.DiagnosticSource.9.0.8\lib\net462\System.Diagnostics.DiagnosticSource.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Memory, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.0\lib\net462\System.Memory.dll</HintPath>
|
||||
<Reference Include="System.Memory, Version=4.0.5.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Memory.4.6.3\lib\net462\System.Memory.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Numerics" />
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.5.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.0\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
<Reference Include="System.Numerics.Vectors, Version=4.1.6.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Numerics.Vectors.4.6.1\lib\net462\System.Numerics.Vectors.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.1\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
<Reference Include="System.Reflection.Metadata, Version=9.0.0.8, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Reflection.Metadata.9.0.8\lib\net462\System.Reflection.Metadata.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime" />
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.1.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.0\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
<Reference Include="System.Runtime.CompilerServices.Unsafe, Version=6.0.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Runtime.CompilerServices.Unsafe.6.1.2\lib\net462\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Runtime.Serialization" />
|
||||
<Reference Include="System.ServiceModel" />
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.1.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.0\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.ValueTuple, Version=4.0.3.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.ValueTuple.4.5.0\lib\net47\System.ValueTuple.dll</HintPath>
|
||||
<Reference Include="System.Threading.Tasks.Extensions, Version=4.2.4.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
|
||||
<HintPath>..\packages\System.Threading.Tasks.Extensions.4.6.3\lib\net462\System.Threading.Tasks.Extensions.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="System.Xml" />
|
||||
</ItemGroup>
|
||||
@@ -177,11 +178,8 @@
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.7.2\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Content Include="ILLink\ILLink.Descriptors.LibraryBuild.xml" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.CodeFixes.dll" />
|
||||
<Analyzer Include="..\packages\MSTest.Analyzers.3.10.3\analyzers\dotnet\cs\MSTest.Analyzers.dll" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets" Condition="Exists('$(VSToolsPath)\TeamTest\Microsoft.TestTools.targets')" />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
@@ -189,13 +187,19 @@
|
||||
<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.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.5.2\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.5.2\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.props'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Testing.Extensions.Telemetry.1.8.3\build\netstandard2.0\Microsoft.Testing.Extensions.Telemetry.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.props'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets'))" />
|
||||
<Error Condition="!Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets'))" />
|
||||
</Target>
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.5.2\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.7.2\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets" Condition="Exists('..\packages\System.ValueTuple.4.6.1\build\net471\System.ValueTuple.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.1.8.3\build\netstandard2.0\Microsoft.Testing.Platform.targets')" />
|
||||
<Import Project="..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets" Condition="Exists('..\packages\Microsoft.Testing.Platform.MSBuild.1.8.3\build\Microsoft.Testing.Platform.MSBuild.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets" Condition="Exists('..\packages\MSTest.TestAdapter.3.10.3\build\net462\MSTest.TestAdapter.targets')" />
|
||||
<Import Project="..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets" Condition="Exists('..\packages\MSTest.TestFramework.3.10.3\build\net462\MSTest.TestFramework.targets')" />
|
||||
</Project>
|
@@ -8,11 +8,11 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Threading.Tasks.Extensions" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.1.0" newVersion="4.2.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.2.4.0" newVersion="4.2.4.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Runtime.CompilerServices.Unsafe" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.1.0" newVersion="6.0.1.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-6.0.3.0" newVersion="6.0.3.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="NuGet.Frameworks" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
@@ -20,27 +20,27 @@
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Collections.Immutable" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="Microsoft.ApplicationInsights" publicKeyToken="31bf3856ad364e35" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.22.0.997" newVersion="2.22.0.997" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-2.23.0.29" newVersion="2.23.0.29" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Diagnostics.DiagnosticSource" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Memory" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.2.0" newVersion="4.0.2.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Reflection.Metadata" publicKeyToken="b03f5f7f11d50a3a" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.1" newVersion="9.0.0.1" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-9.0.0.8" newVersion="9.0.0.8" />
|
||||
</dependentAssembly>
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="System.Buffers" publicKeyToken="cc7b13ffcd2ddd51" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.4.0" newVersion="4.0.4.0" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-4.0.5.0" newVersion="4.0.5.0" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user