SEBWIN-508: Implemented basic JavaScript API.
This commit is contained in:
parent
deccb3340c
commit
0da587e521
19 changed files with 311 additions and 100 deletions
|
@ -13,6 +13,7 @@ using Moq;
|
|||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Browser.Handlers;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
@ -29,6 +30,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IRequestFilter> filter;
|
||||
private Mock<IKeyGenerator> keyGenerator;
|
||||
private Mock<ILogger> logger;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
|
@ -41,11 +43,12 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
{
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
resourceHandler = new ResourceHandler(appConfig, filter.Object, logger.Object, settings, windowSettings, text.Object);
|
||||
resourceHandler = new ResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, settings, windowSettings, text.Object);
|
||||
|
||||
sut = new TestableRequestHandler(appConfig, filter.Object, logger.Object, resourceHandler, settings, windowSettings, text.Object);
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting;
|
|||
using Moq;
|
||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
@ -30,6 +31,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
{
|
||||
private AppConfig appConfig;
|
||||
private Mock<IRequestFilter> filter;
|
||||
private Mock<IKeyGenerator> keyGenerator;
|
||||
private Mock<ILogger> logger;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
|
@ -41,12 +43,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
{
|
||||
appConfig = new AppConfig();
|
||||
filter = new Mock<IRequestFilter>();
|
||||
keyGenerator = new Mock<IKeyGenerator>();
|
||||
logger = new Mock<ILogger>();
|
||||
settings = new BrowserSettings();
|
||||
windowSettings = new WindowSettings();
|
||||
text = new Mock<IText>();
|
||||
|
||||
sut = new TestableResourceHandler(appConfig, filter.Object, logger.Object, settings, windowSettings, text.Object);
|
||||
sut = new TestableResourceHandler(appConfig, filter.Object, keyGenerator.Object, logger.Object, settings, windowSettings, text.Object);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
|
@ -57,11 +60,13 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
var request = new Mock<IRequest>();
|
||||
|
||||
browser.SetupGet(b => b.Address).Returns("http://www.host.org");
|
||||
keyGenerator.Setup(g => g.CalculateBrowserExamKeyHash(It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
keyGenerator.Setup(g => g.CalculateConfigurationKeyHash(It.IsAny<string>())).Returns(new Random().Next().ToString());
|
||||
request.SetupGet(r => r.Headers).Returns(new NameValueCollection());
|
||||
request.SetupGet(r => r.Url).Returns("http://www.host.org");
|
||||
request.SetupSet(r => r.Headers = It.IsAny<NameValueCollection>()).Callback<NameValueCollection>((h) => headers = h);
|
||||
settings.SendConfigurationKey = true;
|
||||
settings.SendExamKey = true;
|
||||
settings.SendBrowserExamKey = true;
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(browser.Object, Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
|
@ -85,7 +90,7 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
request.SetupGet(r => r.Url).Returns("http://www.host.org");
|
||||
request.SetupSet(r => r.Headers = It.IsAny<NameValueCollection>()).Callback<NameValueCollection>((h) => headers = h);
|
||||
settings.SendConfigurationKey = true;
|
||||
settings.SendExamKey = true;
|
||||
settings.SendBrowserExamKey = true;
|
||||
|
||||
var result = sut.OnBeforeResourceLoad(browser.Object, Mock.Of<IBrowser>(), Mock.Of<IFrame>(), request.Object, Mock.Of<IRequestCallback>());
|
||||
|
||||
|
@ -291,10 +296,11 @@ namespace SafeExamBrowser.Browser.UnitTests.Handlers
|
|||
internal TestableResourceHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
IKeyGenerator keyGenerator,
|
||||
ILogger logger,
|
||||
BrowserSettings settings,
|
||||
WindowSettings windowSettings,
|
||||
IText text) : base(appConfig, filter, logger, settings, windowSettings, text)
|
||||
IText text) : base(appConfig, filter, keyGenerator, logger, settings, windowSettings, text)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
@ -38,16 +38,17 @@ namespace SafeExamBrowser.Browser
|
|||
{
|
||||
private int instanceIdCounter = default(int);
|
||||
|
||||
private AppConfig appConfig;
|
||||
private List<BrowserApplicationInstance> instances;
|
||||
private IFileSystemDialog fileSystemDialog;
|
||||
private IHashAlgorithm hashAlgorithm;
|
||||
private INativeMethods nativeMethods;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
private IText text;
|
||||
private IUserInterfaceFactory uiFactory;
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly IFileSystemDialog fileSystemDialog;
|
||||
private readonly IHashAlgorithm hashAlgorithm;
|
||||
private readonly List<BrowserApplicationInstance> instances;
|
||||
private readonly IKeyGenerator keyGenerator;
|
||||
private readonly IModuleLogger logger;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly INativeMethods nativeMethods;
|
||||
private readonly BrowserSettings settings;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
public bool AutoStart { get; private set; }
|
||||
public IconResource Icon { get; private set; }
|
||||
|
@ -65,6 +66,7 @@ namespace SafeExamBrowser.Browser
|
|||
BrowserSettings settings,
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
IKeyGenerator keyGenerator,
|
||||
INativeMethods nativeMethods,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
|
@ -74,10 +76,11 @@ namespace SafeExamBrowser.Browser
|
|||
this.appConfig = appConfig;
|
||||
this.fileSystemDialog = fileSystemDialog;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.instances = new List<BrowserApplicationInstance>();
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.logger = logger;
|
||||
this.messageBox = messageBox;
|
||||
this.nativeMethods = nativeMethods;
|
||||
this.settings = settings;
|
||||
this.text = text;
|
||||
this.uiFactory = uiFactory;
|
||||
|
@ -172,7 +175,19 @@ namespace SafeExamBrowser.Browser
|
|||
var isMainInstance = instances.Count == 0;
|
||||
var instanceLogger = logger.CloneFor($"Browser Instance #{id}");
|
||||
var startUrl = url ?? GenerateStartUrl();
|
||||
var instance = new BrowserApplicationInstance(appConfig, settings, id, isMainInstance, fileSystemDialog, hashAlgorithm, messageBox, instanceLogger, text, uiFactory, startUrl);
|
||||
var instance = new BrowserApplicationInstance(
|
||||
appConfig,
|
||||
settings,
|
||||
id,
|
||||
isMainInstance,
|
||||
fileSystemDialog,
|
||||
hashAlgorithm,
|
||||
keyGenerator,
|
||||
messageBox,
|
||||
instanceLogger,
|
||||
text,
|
||||
uiFactory,
|
||||
startUrl);
|
||||
|
||||
instance.ConfigurationDownloadRequested += (fileName, args) => ConfigurationDownloadRequested?.Invoke(fileName, args);
|
||||
instance.PopupRequested += Instance_PopupRequested;
|
||||
|
|
|
@ -41,19 +41,21 @@ namespace SafeExamBrowser.Browser
|
|||
{
|
||||
private const double ZOOM_FACTOR = 0.2;
|
||||
|
||||
private AppConfig appConfig;
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly IFileSystemDialog fileSystemDialog;
|
||||
private readonly IHashAlgorithm hashAlgorithm;
|
||||
private readonly HttpClient httpClient;
|
||||
private readonly IKeyGenerator keyGenerator;
|
||||
private readonly IModuleLogger logger;
|
||||
private readonly IMessageBox messageBox;
|
||||
private readonly IText text;
|
||||
private readonly IUserInterfaceFactory uiFactory;
|
||||
|
||||
private IBrowserControl control;
|
||||
private IBrowserWindow window;
|
||||
private HttpClient httpClient;
|
||||
private bool isMainInstance;
|
||||
private IFileSystemDialog fileSystemDialog;
|
||||
private IHashAlgorithm hashAlgorithm;
|
||||
private IMessageBox messageBox;
|
||||
private IModuleLogger logger;
|
||||
private BrowserSettings settings;
|
||||
private string startUrl;
|
||||
private IText text;
|
||||
private IUserInterfaceFactory uiFactory;
|
||||
private double zoomLevel;
|
||||
|
||||
private WindowSettings WindowSettings
|
||||
|
@ -84,6 +86,7 @@ namespace SafeExamBrowser.Browser
|
|||
bool isMainInstance,
|
||||
IFileSystemDialog fileSystemDialog,
|
||||
IHashAlgorithm hashAlgorithm,
|
||||
IKeyGenerator keyGenerator,
|
||||
IMessageBox messageBox,
|
||||
IModuleLogger logger,
|
||||
IText text,
|
||||
|
@ -96,6 +99,7 @@ namespace SafeExamBrowser.Browser
|
|||
this.isMainInstance = isMainInstance;
|
||||
this.fileSystemDialog = fileSystemDialog;
|
||||
this.hashAlgorithm = hashAlgorithm;
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.messageBox = messageBox;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
|
@ -130,9 +134,11 @@ namespace SafeExamBrowser.Browser
|
|||
var downloadHandler = new DownloadHandler(appConfig, downloadLogger, settings, WindowSettings);
|
||||
var keyboardHandler = new KeyboardHandler();
|
||||
var lifeSpanHandler = new LifeSpanHandler();
|
||||
var renderProcessMessageLogger = logger.CloneFor($"{nameof(RenderProcessMessageHandler)} #{Id}");
|
||||
var renderProcessMessageHandler = new RenderProcessMessageHandler(appConfig, renderProcessMessageLogger, keyGenerator, text);
|
||||
var requestFilter = new RequestFilter();
|
||||
var requestLogger = logger.CloneFor($"{nameof(RequestHandler)} #{Id}");
|
||||
var resourceHandler = new ResourceHandler(appConfig, requestFilter, logger, settings, WindowSettings, text);
|
||||
var resourceHandler = new ResourceHandler(appConfig, requestFilter, keyGenerator, logger, settings, WindowSettings, text);
|
||||
var requestHandler = new RequestHandler(appConfig, requestFilter, requestLogger, resourceHandler, settings, WindowSettings, text);
|
||||
|
||||
Icon = new BrowserIconResource();
|
||||
|
@ -162,6 +168,7 @@ namespace SafeExamBrowser.Browser
|
|||
downloadHandler,
|
||||
keyboardHandler,
|
||||
lifeSpanHandler,
|
||||
renderProcessMessageHandler,
|
||||
requestHandler,
|
||||
startUrl);
|
||||
control.AddressChanged += Control_AddressChanged;
|
||||
|
|
|
@ -16,13 +16,14 @@ namespace SafeExamBrowser.Browser
|
|||
{
|
||||
internal class BrowserControl : ChromiumWebBrowser, IBrowserControl
|
||||
{
|
||||
private IContextMenuHandler contextMenuHandler;
|
||||
private IDialogHandler dialogHandler;
|
||||
private IDisplayHandler displayHandler;
|
||||
private IDownloadHandler downloadHandler;
|
||||
private IKeyboardHandler keyboardHandler;
|
||||
private ILifeSpanHandler lifeSpanHandler;
|
||||
private IRequestHandler requestHandler;
|
||||
private readonly IContextMenuHandler contextMenuHandler;
|
||||
private readonly IDialogHandler dialogHandler;
|
||||
private readonly IDisplayHandler displayHandler;
|
||||
private readonly IDownloadHandler downloadHandler;
|
||||
private readonly IKeyboardHandler keyboardHandler;
|
||||
private readonly ILifeSpanHandler lifeSpanHandler;
|
||||
private readonly IRenderProcessMessageHandler renderProcessMessageHandler;
|
||||
private readonly IRequestHandler requestHandler;
|
||||
|
||||
private AddressChangedEventHandler addressChanged;
|
||||
private LoadFailedEventHandler loadFailed;
|
||||
|
@ -63,6 +64,7 @@ namespace SafeExamBrowser.Browser
|
|||
IDownloadHandler downloadHandler,
|
||||
IKeyboardHandler keyboardHandler,
|
||||
ILifeSpanHandler lifeSpanHandler,
|
||||
IRenderProcessMessageHandler renderProcessMessageHandler,
|
||||
IRequestHandler requestHandler,
|
||||
string url) : base(url)
|
||||
{
|
||||
|
@ -72,6 +74,7 @@ namespace SafeExamBrowser.Browser
|
|||
this.downloadHandler = downloadHandler;
|
||||
this.keyboardHandler = keyboardHandler;
|
||||
this.lifeSpanHandler = lifeSpanHandler;
|
||||
this.renderProcessMessageHandler = renderProcessMessageHandler;
|
||||
this.requestHandler = requestHandler;
|
||||
}
|
||||
|
||||
|
@ -97,6 +100,7 @@ namespace SafeExamBrowser.Browser
|
|||
KeyboardHandler = keyboardHandler;
|
||||
LifeSpanHandler = lifeSpanHandler;
|
||||
MenuHandler = contextMenuHandler;
|
||||
RenderProcessMessageHandler = renderProcessMessageHandler;
|
||||
RequestHandler = requestHandler;
|
||||
}
|
||||
|
||||
|
|
8
SafeExamBrowser.Browser/Content/Api.js
Normal file
8
SafeExamBrowser.Browser/Content/Api.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
SafeExamBrowser = {
|
||||
version: 'SEB_Windows_%%_VERSION_%%',
|
||||
security: {
|
||||
browserExamKey: '%%_BEK_%%',
|
||||
configKey: '%%_CK_%%',
|
||||
updateKeys: (callback) => callback()
|
||||
}
|
||||
}
|
|
@ -10,21 +10,45 @@ using System.IO;
|
|||
using System.Reflection;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Pages
|
||||
namespace SafeExamBrowser.Browser.Content
|
||||
{
|
||||
internal class HtmlLoader
|
||||
internal class ContentLoader
|
||||
{
|
||||
private string api;
|
||||
private IText text;
|
||||
|
||||
internal HtmlLoader(IText text)
|
||||
internal ContentLoader(IText text)
|
||||
{
|
||||
this.text = text;
|
||||
}
|
||||
|
||||
internal string LoadApi(string browserExamKey, string configurationKey, string version)
|
||||
{
|
||||
if (api == default(string))
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(ContentLoader));
|
||||
var path = $"{typeof(ContentLoader).Namespace}.Api.js";
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(path))
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
api = reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
|
||||
var js = api;
|
||||
|
||||
js = js.Replace("%%_BEK_%%", browserExamKey);
|
||||
js = js.Replace("%%_CK_%%", configurationKey);
|
||||
js = js.Replace("%%_VERSION_%%", version);
|
||||
|
||||
return js;
|
||||
}
|
||||
|
||||
internal string LoadBlockedContent()
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(HtmlLoader));
|
||||
var path = $"{typeof(HtmlLoader).Namespace}.BlockedContent.html";
|
||||
var assembly = Assembly.GetAssembly(typeof(ContentLoader));
|
||||
var path = $"{typeof(ContentLoader).Namespace}.BlockedContent.html";
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(path))
|
||||
using (var reader = new StreamReader(stream))
|
||||
|
@ -39,8 +63,8 @@ namespace SafeExamBrowser.Browser.Pages
|
|||
|
||||
internal string LoadBlockedPage()
|
||||
{
|
||||
var assembly = Assembly.GetAssembly(typeof(HtmlLoader));
|
||||
var path = $"{typeof(HtmlLoader).Namespace}.BlockedPage.html";
|
||||
var assembly = Assembly.GetAssembly(typeof(ContentLoader));
|
||||
var path = $"{typeof(ContentLoader).Namespace}.BlockedPage.html";
|
||||
|
||||
using (var stream = assembly.GetManifestResourceStream(path))
|
||||
using (var reader = new StreamReader(stream))
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* 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 SafeExamBrowser.Browser.Content;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
|
||||
namespace SafeExamBrowser.Browser.Handlers
|
||||
{
|
||||
internal class RenderProcessMessageHandler : IRenderProcessMessageHandler
|
||||
{
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly ContentLoader contentLoader;
|
||||
private readonly ILogger logger;
|
||||
private readonly IKeyGenerator generator;
|
||||
|
||||
internal RenderProcessMessageHandler(AppConfig appConfig, ILogger logger, IKeyGenerator generator, IText text)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.contentLoader = new ContentLoader(text);
|
||||
this.logger = logger;
|
||||
this.generator = generator;
|
||||
}
|
||||
|
||||
public void OnContextCreated(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
var browserExamKey = generator.CalculateBrowserExamKeyHash(webBrowser.Address);
|
||||
var configurationKey = generator.CalculateConfigurationKeyHash(webBrowser.Address);
|
||||
var api = contentLoader.LoadApi(browserExamKey, configurationKey, appConfig.ProgramBuildVersion);
|
||||
|
||||
frame.ExecuteJavaScriptAsync(api);
|
||||
}
|
||||
|
||||
public void OnContextReleased(IWebBrowser webBrowser, IBrowser browser, IFrame frame)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnFocusedNodeChanged(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IDomNode node)
|
||||
{
|
||||
}
|
||||
|
||||
public void OnUncaughtException(IWebBrowser webBrowser, IBrowser browser, IFrame frame, JavascriptException exception)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,16 +12,15 @@ using System.Linq;
|
|||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Mime;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
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.Pages;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.I18n.Contracts;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings.Browser;
|
||||
|
@ -33,33 +32,34 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
{
|
||||
internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler
|
||||
{
|
||||
private SHA256Managed algorithm;
|
||||
private AppConfig appConfig;
|
||||
private string browserExamKey;
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly ContentLoader contentLoader;
|
||||
private readonly IRequestFilter filter;
|
||||
private readonly IKeyGenerator keyGenerator;
|
||||
private readonly ILogger logger;
|
||||
private readonly BrowserSettings settings;
|
||||
private readonly IText text;
|
||||
private readonly WindowSettings windowSettings;
|
||||
|
||||
private IResourceHandler contentHandler;
|
||||
private IRequestFilter filter;
|
||||
private HtmlLoader htmlLoader;
|
||||
private ILogger logger;
|
||||
private IResourceHandler pageHandler;
|
||||
private string sessionIdentifier;
|
||||
private BrowserSettings settings;
|
||||
private WindowSettings windowSettings;
|
||||
private IText text;
|
||||
|
||||
internal event SessionIdentifierDetectedEventHandler SessionIdentifierDetected;
|
||||
|
||||
internal ResourceHandler(
|
||||
AppConfig appConfig,
|
||||
IRequestFilter filter,
|
||||
IKeyGenerator keyGenerator,
|
||||
ILogger logger,
|
||||
BrowserSettings settings,
|
||||
WindowSettings windowSettings,
|
||||
IText text)
|
||||
{
|
||||
this.appConfig = appConfig;
|
||||
this.algorithm = new SHA256Managed();
|
||||
this.filter = filter;
|
||||
this.htmlLoader = new HtmlLoader(text);
|
||||
this.contentLoader = new ContentLoader(text);
|
||||
this.keyGenerator = keyGenerator;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
this.windowSettings = windowSettings;
|
||||
|
@ -123,22 +123,15 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
if (pageUrl?.Host?.Equals(requestUrl?.Host) == true)
|
||||
{
|
||||
var headers = new NameValueCollection(request.Headers);
|
||||
var urlWithoutFragment = request.Url.Split('#')[0];
|
||||
|
||||
if (settings.SendConfigurationKey)
|
||||
{
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.ConfigurationKey));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
headers["X-SafeExamBrowser-ConfigKeyHash"] = key;
|
||||
headers["X-SafeExamBrowser-ConfigKeyHash"] = keyGenerator.CalculateConfigurationKeyHash(request.Url);
|
||||
}
|
||||
|
||||
if (settings.SendExamKey)
|
||||
if (settings.SendBrowserExamKey)
|
||||
{
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey())));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
headers["X-SafeExamBrowser-RequestHash"] = key;
|
||||
headers["X-SafeExamBrowser-RequestHash"] = keyGenerator.CalculateBrowserExamKeyHash(request.Url);
|
||||
}
|
||||
|
||||
request.Headers = headers;
|
||||
|
@ -169,27 +162,6 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
return block;
|
||||
}
|
||||
|
||||
private string ComputeBrowserExamKey()
|
||||
{
|
||||
var salt = settings.ExamKeySalt;
|
||||
|
||||
if (salt == default(byte[]))
|
||||
{
|
||||
salt = new byte[0];
|
||||
logger.Warn("The current configuration does not contain a salt value for the browser exam key!");
|
||||
}
|
||||
|
||||
using (var algorithm = new HMACSHA256(salt))
|
||||
{
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + settings.ConfigurationKey));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
browserExamKey = key;
|
||||
|
||||
return browserExamKey;
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsMailtoUrl(string url)
|
||||
{
|
||||
return url.StartsWith(Uri.UriSchemeMailto);
|
||||
|
@ -234,12 +206,12 @@ namespace SafeExamBrowser.Browser.Handlers
|
|||
{
|
||||
if (contentHandler == default(IResourceHandler))
|
||||
{
|
||||
contentHandler = CefSharp.ResourceHandler.FromString(htmlLoader.LoadBlockedContent());
|
||||
contentHandler = CefSharp.ResourceHandler.FromString(contentLoader.LoadBlockedContent());
|
||||
}
|
||||
|
||||
if (pageHandler == default(IResourceHandler))
|
||||
{
|
||||
pageHandler = CefSharp.ResourceHandler.FromString(htmlLoader.LoadBlockedPage());
|
||||
pageHandler = CefSharp.ResourceHandler.FromString(contentLoader.LoadBlockedPage());
|
||||
}
|
||||
|
||||
switch (resourceType)
|
||||
|
|
|
@ -104,9 +104,10 @@
|
|||
<Compile Include="Handlers\DownloadHandler.cs" />
|
||||
<Compile Include="Handlers\KeyboardHandler.cs" />
|
||||
<Compile Include="Handlers\LifeSpanHandler.cs" />
|
||||
<Compile Include="Handlers\RenderProcessMessageHandler.cs" />
|
||||
<Compile Include="Handlers\RequestHandler.cs" />
|
||||
<Compile Include="Handlers\ResourceHandler.cs" />
|
||||
<Compile Include="Pages\HtmlLoader.cs" />
|
||||
<Compile Include="Content\ContentLoader.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
@ -148,8 +149,8 @@
|
|||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Pages\BlockedContent.html" />
|
||||
<EmbeddedResource Include="Pages\BlockedPage.html" />
|
||||
<EmbeddedResource Include="Content\BlockedContent.html" />
|
||||
<EmbeddedResource Include="Content\BlockedPage.html" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
|
@ -157,6 +158,9 @@
|
|||
<SubType>Designer</SubType>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Content\Api.js" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<PropertyGroup>
|
||||
<PostBuildEvent>
|
||||
|
|
|
@ -209,8 +209,19 @@ namespace SafeExamBrowser.Client
|
|||
private IOperation BuildBrowserOperation()
|
||||
{
|
||||
var fileSystemDialog = BuildFileSystemDialog();
|
||||
var keyGenerator = new KeyGenerator(context.AppConfig, ModuleLogger(nameof(KeyGenerator)), context.Settings);
|
||||
var moduleLogger = ModuleLogger(nameof(BrowserApplication));
|
||||
var browser = new BrowserApplication(context.AppConfig, context.Settings.Browser, fileSystemDialog, new HashAlgorithm(), nativeMethods, messageBox, moduleLogger, text, uiFactory);
|
||||
var browser = new BrowserApplication(
|
||||
context.AppConfig,
|
||||
context.Settings.Browser,
|
||||
fileSystemDialog,
|
||||
new HashAlgorithm(),
|
||||
keyGenerator,
|
||||
nativeMethods,
|
||||
messageBox,
|
||||
moduleLogger,
|
||||
text,
|
||||
uiFactory);
|
||||
var operation = new BrowserOperation(actionCenter, context, logger, taskbar, taskview, uiFactory);
|
||||
|
||||
context.Browser = browser;
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* 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.Configuration.Contracts.Cryptography
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides funcionality to calculate keys for integrity checks.
|
||||
/// </summary>
|
||||
public interface IKeyGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// Calculates the hash value of the browser exam key (BEK) for the given URL.
|
||||
/// </summary>
|
||||
string CalculateBrowserExamKeyHash(string url);
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the hash value of the configuration key (CK) for the given URL.
|
||||
/// </summary>
|
||||
string CalculateConfigurationKeyHash(string url);
|
||||
}
|
||||
}
|
|
@ -59,6 +59,7 @@
|
|||
<Compile Include="Cryptography\EncryptionParameters.cs" />
|
||||
<Compile Include="Cryptography\ICertificateStore.cs" />
|
||||
<Compile Include="Cryptography\IHashAlgorithm.cs" />
|
||||
<Compile Include="Cryptography\IKeyGenerator.cs" />
|
||||
<Compile Include="Cryptography\IPasswordEncryption.cs" />
|
||||
<Compile Include="Cryptography\IPublicKeyEncryption.cs" />
|
||||
<Compile Include="Cryptography\PasswordParameters.cs" />
|
||||
|
|
|
@ -346,7 +346,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
{
|
||||
if (value is byte[] salt)
|
||||
{
|
||||
settings.Browser.ExamKeySalt = salt;
|
||||
settings.Browser.BrowserExamKeySalt = salt;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,7 +487,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
|||
if (value is bool send)
|
||||
{
|
||||
settings.Browser.SendConfigurationKey = send;
|
||||
settings.Browser.SendExamKey = send;
|
||||
settings.Browser.SendBrowserExamKey = send;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
75
SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs
Normal file
75
SafeExamBrowser.Configuration/Cryptography/KeyGenerator.cs
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2021 ETH Zürich, Educational Development and Technology (LET)
|
||||
*
|
||||
* 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.Security.Cryptography;
|
||||
using System.Text;
|
||||
using SafeExamBrowser.Configuration.Contracts;
|
||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||
using SafeExamBrowser.Logging.Contracts;
|
||||
using SafeExamBrowser.Settings;
|
||||
|
||||
namespace SafeExamBrowser.Configuration.Cryptography
|
||||
{
|
||||
public class KeyGenerator : IKeyGenerator
|
||||
{
|
||||
private readonly SHA256Managed algorithm;
|
||||
private readonly AppConfig appConfig;
|
||||
private readonly ILogger logger;
|
||||
private readonly AppSettings settings;
|
||||
|
||||
private string browserExamKey;
|
||||
|
||||
public KeyGenerator(AppConfig appConfig, ILogger logger, AppSettings settings)
|
||||
{
|
||||
this.algorithm = new SHA256Managed();
|
||||
this.appConfig = appConfig;
|
||||
this.logger = logger;
|
||||
this.settings = settings;
|
||||
}
|
||||
|
||||
public string CalculateBrowserExamKeyHash(string url)
|
||||
{
|
||||
var urlWithoutFragment = url.Split('#')[0];
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + (browserExamKey ?? ComputeBrowserExamKey())));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
public string CalculateConfigurationKeyHash(string url)
|
||||
{
|
||||
var urlWithoutFragment = url.Split('#')[0];
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.Browser.ConfigurationKey));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
private string ComputeBrowserExamKey()
|
||||
{
|
||||
var salt = settings.Browser.BrowserExamKeySalt;
|
||||
|
||||
if (salt == default(byte[]))
|
||||
{
|
||||
salt = new byte[0];
|
||||
logger.Warn("The current configuration does not contain a salt value for the browser exam key!");
|
||||
}
|
||||
|
||||
using (var algorithm = new HMACSHA256(salt))
|
||||
{
|
||||
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(appConfig.CodeSignatureHash + appConfig.ProgramBuildVersion + settings.Browser.ConfigurationKey));
|
||||
var key = BitConverter.ToString(hash).ToLower().Replace("-", string.Empty);
|
||||
|
||||
browserExamKey = key;
|
||||
|
||||
return browserExamKey;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -74,6 +74,7 @@
|
|||
<Compile Include="ConfigurationData\Keys.cs" />
|
||||
<Compile Include="ConfigurationData\DataValues.cs" />
|
||||
<Compile Include="Cryptography\CertificateStore.cs" />
|
||||
<Compile Include="Cryptography\KeyGenerator.cs" />
|
||||
<Compile Include="DataCompression\GZipCompressor.cs" />
|
||||
<Compile Include="Cryptography\PasswordEncryption.cs" />
|
||||
<Compile Include="Cryptography\PublicKeyEncryption.cs" />
|
||||
|
|
|
@ -66,6 +66,11 @@ namespace SafeExamBrowser.Settings.Browser
|
|||
/// </summary>
|
||||
public bool AllowUploads { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The salt value for the calculation of the browser exam key which is used for integrity checks with server applications (see also <see cref="SendBrowserExamKey"/>).
|
||||
/// </summary>
|
||||
public byte[] BrowserExamKeySalt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The configuration key used for integrity checks with server applications (see also <see cref="SendConfigurationKey"/>).
|
||||
/// </summary>
|
||||
|
@ -108,11 +113,6 @@ namespace SafeExamBrowser.Settings.Browser
|
|||
/// </summary>
|
||||
public bool EnableBrowser { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The salt value for the calculation of the exam key which is used for integrity checks with server applications (see also <see cref="SendExamKey"/>).
|
||||
/// </summary>
|
||||
public byte[] ExamKeySalt { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The settings to be used for the browser request filter.
|
||||
/// </summary>
|
||||
|
@ -169,9 +169,9 @@ namespace SafeExamBrowser.Settings.Browser
|
|||
public bool SendConfigurationKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the exam key header is sent with every HTTP request (see also <see cref="ExamKeySalt"/>).
|
||||
/// Determines whether the browser exam key header is sent with every HTTP request (see also <see cref="BrowserExamKeySalt"/>).
|
||||
/// </summary>
|
||||
public bool SendExamKey { get; set; }
|
||||
public bool SendBrowserExamKey { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The URL with which the main browser window will be loaded.
|
||||
|
|
Loading…
Reference in a new issue