SEBWIN-309: Implemented scaffolding for Configuration Key.
This commit is contained in:
parent
45e1b001e3
commit
5e131289b0
8 changed files with 188 additions and 8 deletions
|
@ -33,7 +33,7 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
this.resourceHandler = new ResourceHandler(appConfig, settings.Filter, filter, logger, text);
|
this.resourceHandler = new ResourceHandler(appConfig, settings, filter, logger, text);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
|
protected override bool GetAuthCredentials(IWebBrowser webBrowser, IBrowser browser, string originUrl, bool isProxy, string host, int port, string realm, string scheme, IAuthCallback callback)
|
||||||
|
|
|
@ -10,30 +10,34 @@ using System;
|
||||||
using System.Collections.Specialized;
|
using System.Collections.Specialized;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
using CefSharp;
|
using CefSharp;
|
||||||
using SafeExamBrowser.Browser.Contracts.Filters;
|
using SafeExamBrowser.Browser.Contracts.Filters;
|
||||||
using SafeExamBrowser.Browser.Filters;
|
using SafeExamBrowser.Browser.Filters;
|
||||||
using SafeExamBrowser.Configuration.Contracts;
|
using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.I18n.Contracts;
|
using SafeExamBrowser.I18n.Contracts;
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
using SafeExamBrowser.Settings.Browser;
|
|
||||||
using SafeExamBrowser.Settings.Browser.Filter;
|
using SafeExamBrowser.Settings.Browser.Filter;
|
||||||
|
using BrowserSettings = SafeExamBrowser.Settings.Browser.BrowserSettings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Browser.Handlers
|
namespace SafeExamBrowser.Browser.Handlers
|
||||||
{
|
{
|
||||||
internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler
|
internal class ResourceHandler : CefSharp.Handler.ResourceRequestHandler
|
||||||
{
|
{
|
||||||
private AppConfig appConfig;
|
private AppConfig appConfig;
|
||||||
private FilterSettings settings;
|
private SHA256Managed algorithm;
|
||||||
|
private BrowserSettings settings;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
private IRequestFilter filter;
|
private IRequestFilter filter;
|
||||||
private IResourceHandler contentHandler;
|
private IResourceHandler contentHandler;
|
||||||
private IResourceHandler pageHandler;
|
private IResourceHandler pageHandler;
|
||||||
private IText text;
|
private IText text;
|
||||||
|
|
||||||
internal ResourceHandler(AppConfig appConfig, FilterSettings settings, IRequestFilter filter, ILogger logger, IText text)
|
internal ResourceHandler(AppConfig appConfig, BrowserSettings settings, IRequestFilter filter, ILogger logger, IText text)
|
||||||
{
|
{
|
||||||
this.appConfig = appConfig;
|
this.appConfig = appConfig;
|
||||||
|
this.algorithm = new SHA256Managed();
|
||||||
this.filter = filter;
|
this.filter = filter;
|
||||||
this.logger = logger;
|
this.logger = logger;
|
||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
|
@ -52,14 +56,19 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
|
|
||||||
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
|
protected override CefReturnValue OnBeforeResourceLoad(IWebBrowser webBrowser, IBrowser browser, IFrame frame, IRequest request, IRequestCallback callback)
|
||||||
{
|
{
|
||||||
|
if (IsMailtoUrl(request.Url))
|
||||||
|
{
|
||||||
|
return CefReturnValue.Cancel;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser
|
// TODO: CEF does not yet support intercepting requests from service workers, thus the user agent must be statically set at browser
|
||||||
// startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below
|
// startup for now. Once CEF has full support of service workers, the static user agent should be removed and the method below
|
||||||
// reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development.
|
// reactivated. See https://bitbucket.org/chromiumembedded/cef/issues/2622 for the current status of development.
|
||||||
// AppendCustomUserAgent(request);
|
// AppendCustomUserAgent(request);
|
||||||
|
|
||||||
if (IsMailtoUrl(request.Url))
|
if (settings.SendCustomHeaders)
|
||||||
{
|
{
|
||||||
return CefReturnValue.Cancel;
|
AppendCustomHeaders(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReplaceSebScheme(request);
|
ReplaceSebScheme(request);
|
||||||
|
@ -76,9 +85,22 @@ namespace SafeExamBrowser.Browser.Handlers
|
||||||
request.Headers = headers;
|
request.Headers = headers;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void AppendCustomHeaders(IRequest request)
|
||||||
|
{
|
||||||
|
var headers = new NameValueCollection(request.Headers);
|
||||||
|
var urlWithoutFragment = request.Url.Split('#')[0];
|
||||||
|
var hash = algorithm.ComputeHash(Encoding.UTF8.GetBytes(urlWithoutFragment + settings.HashValue));
|
||||||
|
var configurationKey = BitConverter.ToString(hash).Replace("-", string.Empty);
|
||||||
|
|
||||||
|
// TODO: Implement Browser Exam Key calculation.
|
||||||
|
// headers["X-SafeExamBrowser-RequestHash"] = ...;
|
||||||
|
headers["X-SafeExamBrowser-ConfigKeyHash"] = configurationKey;
|
||||||
|
request.Headers = headers;
|
||||||
|
}
|
||||||
|
|
||||||
private bool Block(IRequest request)
|
private bool Block(IRequest request)
|
||||||
{
|
{
|
||||||
if (settings.ProcessContentRequests)
|
if (settings.Filter.ProcessContentRequests)
|
||||||
{
|
{
|
||||||
var result = filter.Process(new Request { Url = request.Url });
|
var result = filter.Process(new Request { Url = request.Url });
|
||||||
var block = result == FilterResult.Block;
|
var block = result == FilterResult.Block;
|
||||||
|
|
|
@ -96,6 +96,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
case Keys.Browser.ShowToolbar:
|
case Keys.Browser.ShowToolbar:
|
||||||
MapShowToolbar(settings, value);
|
MapShowToolbar(settings, value);
|
||||||
break;
|
break;
|
||||||
|
case Keys.Browser.SendCustomHeaders:
|
||||||
|
MapSendCustomHeaders(settings, value);
|
||||||
|
break;
|
||||||
case Keys.Browser.StartUrl:
|
case Keys.Browser.StartUrl:
|
||||||
MapStartUrl(settings, value);
|
MapStartUrl(settings, value);
|
||||||
break;
|
break;
|
||||||
|
@ -275,6 +278,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void MapSendCustomHeaders(AppSettings settings, object value)
|
||||||
|
{
|
||||||
|
if (value is bool send)
|
||||||
|
{
|
||||||
|
settings.Browser.SendCustomHeaders = send;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void MapStartUrl(AppSettings settings, object value)
|
private void MapStartUrl(AppSettings settings, object value)
|
||||||
{
|
{
|
||||||
if (value is string url)
|
if (value is string url)
|
||||||
|
|
131
SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs
Normal file
131
SafeExamBrowser.Configuration/ConfigurationData/DataProcessor.cs
Normal file
|
@ -0,0 +1,131 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020 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.Collections.Generic;
|
||||||
|
using System.Globalization;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text;
|
||||||
|
using SafeExamBrowser.Settings;
|
||||||
|
|
||||||
|
namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
|
{
|
||||||
|
internal class DataProcessor
|
||||||
|
{
|
||||||
|
internal void Process(IDictionary<string, object> rawData, AppSettings settings)
|
||||||
|
{
|
||||||
|
CalculateHashValue(rawData, settings);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CalculateHashValue(IDictionary<string, object> rawData, AppSettings settings)
|
||||||
|
{
|
||||||
|
using (var algorithm = new SHA256Managed())
|
||||||
|
using (var stream = new MemoryStream())
|
||||||
|
using (var writer = new StreamWriter(stream, Encoding.UTF8))
|
||||||
|
{
|
||||||
|
Serialize(rawData, writer);
|
||||||
|
writer.Flush();
|
||||||
|
stream.Seek(0, SeekOrigin.Begin);
|
||||||
|
|
||||||
|
var hash = algorithm.ComputeHash(stream);
|
||||||
|
var hashString = BitConverter.ToString(hash).Replace("-", string.Empty);
|
||||||
|
|
||||||
|
settings.Browser.HashValue = hashString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Serialize(IDictionary<string, object> dictionary, StreamWriter stream)
|
||||||
|
{
|
||||||
|
var orderedByKey = dictionary.OrderBy(d => d.Key, StringComparer.OrdinalIgnoreCase).ToList();
|
||||||
|
|
||||||
|
stream.Write('{');
|
||||||
|
|
||||||
|
foreach (var kvp in orderedByKey)
|
||||||
|
{
|
||||||
|
var process = true;
|
||||||
|
|
||||||
|
process &= !kvp.Key.Equals(Keys.General.OriginatorVersion, StringComparison.OrdinalIgnoreCase);
|
||||||
|
process &= !(kvp.Value is IDictionary<string, object> d) || d.Any();
|
||||||
|
|
||||||
|
if (process)
|
||||||
|
{
|
||||||
|
stream.Write('"');
|
||||||
|
stream.Write(kvp.Key);
|
||||||
|
stream.Write('"');
|
||||||
|
stream.Write(':');
|
||||||
|
Serialize(kvp.Value, stream);
|
||||||
|
|
||||||
|
if (kvp.Key != orderedByKey.Last().Key)
|
||||||
|
{
|
||||||
|
stream.Write(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Write('}');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Serialize(IList<object> list, StreamWriter stream)
|
||||||
|
{
|
||||||
|
stream.Write('[');
|
||||||
|
|
||||||
|
foreach (var item in list)
|
||||||
|
{
|
||||||
|
Serialize(item, stream);
|
||||||
|
|
||||||
|
if (item != list.Last())
|
||||||
|
{
|
||||||
|
stream.Write(',');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.Write(']');
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Serialize(object value, StreamWriter stream)
|
||||||
|
{
|
||||||
|
switch (value)
|
||||||
|
{
|
||||||
|
case IDictionary<string, object> dictionary:
|
||||||
|
Serialize(dictionary, stream);
|
||||||
|
break;
|
||||||
|
case IList<object> list:
|
||||||
|
Serialize(list, stream);
|
||||||
|
break;
|
||||||
|
case byte[] data:
|
||||||
|
stream.Write('"');
|
||||||
|
stream.Write(Convert.ToBase64String(data));
|
||||||
|
stream.Write('"');
|
||||||
|
break;
|
||||||
|
case DateTime date:
|
||||||
|
stream.Write(date.ToString("o"));
|
||||||
|
break;
|
||||||
|
case bool boolean:
|
||||||
|
stream.Write(boolean.ToString().ToLower());
|
||||||
|
break;
|
||||||
|
case int integer:
|
||||||
|
stream.Write(integer.ToString(NumberFormatInfo.InvariantInfo));
|
||||||
|
break;
|
||||||
|
case double number:
|
||||||
|
stream.Write(number.ToString(NumberFormatInfo.InvariantInfo));
|
||||||
|
break;
|
||||||
|
case string text:
|
||||||
|
stream.Write('"');
|
||||||
|
stream.Write(text);
|
||||||
|
stream.Write('"');
|
||||||
|
break;
|
||||||
|
case null:
|
||||||
|
stream.Write('"');
|
||||||
|
stream.Write('"');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -52,6 +52,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
internal const string QuitUrl = "quitURL";
|
internal const string QuitUrl = "quitURL";
|
||||||
internal const string QuitUrlConfirmation = "quitURLConfirm";
|
internal const string QuitUrlConfirmation = "quitURLConfirm";
|
||||||
internal const string ShowToolbar = "enableBrowserWindowToolbar";
|
internal const string ShowToolbar = "enableBrowserWindowToolbar";
|
||||||
|
internal const string SendCustomHeaders = "sendBrowserExamKey";
|
||||||
internal const string StartUrl = "startURL";
|
internal const string StartUrl = "startURL";
|
||||||
internal const string UserAgentModeDesktop = "browserUserAgentWinDesktopMode";
|
internal const string UserAgentModeDesktop = "browserUserAgentWinDesktopMode";
|
||||||
internal const string UserAgentModeMobile = "browserUserAgentWinTouchMode";
|
internal const string UserAgentModeMobile = "browserUserAgentWinTouchMode";
|
||||||
|
@ -152,6 +153,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData
|
||||||
{
|
{
|
||||||
internal const string AllowApplicationLog = "allowApplicationLog";
|
internal const string AllowApplicationLog = "allowApplicationLog";
|
||||||
internal const string LogLevel = "logLevel";
|
internal const string LogLevel = "logLevel";
|
||||||
|
internal const string OriginatorVersion = "originatorVersion";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal static class Keyboard
|
internal static class Keyboard
|
||||||
|
|
|
@ -15,8 +15,8 @@ using SafeExamBrowser.Configuration.Contracts;
|
||||||
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
using SafeExamBrowser.Configuration.Contracts.Cryptography;
|
||||||
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
using SafeExamBrowser.Configuration.Contracts.DataFormats;
|
||||||
using SafeExamBrowser.Configuration.Contracts.DataResources;
|
using SafeExamBrowser.Configuration.Contracts.DataResources;
|
||||||
using SafeExamBrowser.Settings;
|
|
||||||
using SafeExamBrowser.Logging.Contracts;
|
using SafeExamBrowser.Logging.Contracts;
|
||||||
|
using SafeExamBrowser.Settings;
|
||||||
|
|
||||||
namespace SafeExamBrowser.Configuration
|
namespace SafeExamBrowser.Configuration
|
||||||
{
|
{
|
||||||
|
@ -26,6 +26,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
private IList<IDataParser> dataParsers;
|
private IList<IDataParser> dataParsers;
|
||||||
private IList<IDataSerializer> dataSerializers;
|
private IList<IDataSerializer> dataSerializers;
|
||||||
private DataMapper dataMapper;
|
private DataMapper dataMapper;
|
||||||
|
private DataProcessor dataProcessor;
|
||||||
private DataValues dataValues;
|
private DataValues dataValues;
|
||||||
private IHashAlgorithm hashAlgorithm;
|
private IHashAlgorithm hashAlgorithm;
|
||||||
private ILogger logger;
|
private ILogger logger;
|
||||||
|
@ -49,6 +50,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
dataParsers = new List<IDataParser>();
|
dataParsers = new List<IDataParser>();
|
||||||
dataSerializers = new List<IDataSerializer>();
|
dataSerializers = new List<IDataSerializer>();
|
||||||
dataMapper = new DataMapper();
|
dataMapper = new DataMapper();
|
||||||
|
dataProcessor = new DataProcessor();
|
||||||
dataValues = new DataValues(executablePath, programBuild, programCopyright, programTitle, programVersion);
|
dataValues = new DataValues(executablePath, programBuild, programCopyright, programTitle, programVersion);
|
||||||
resourceLoaders = new List<IResourceLoader>();
|
resourceLoaders = new List<IResourceLoader>();
|
||||||
resourceSavers = new List<IResourceSaver>();
|
resourceSavers = new List<IResourceSaver>();
|
||||||
|
@ -144,6 +146,7 @@ namespace SafeExamBrowser.Configuration
|
||||||
if (status == LoadStatus.Success)
|
if (status == LoadStatus.Success)
|
||||||
{
|
{
|
||||||
dataMapper.MapRawDataToSettings(data, settings);
|
dataMapper.MapRawDataToSettings(data, settings);
|
||||||
|
dataProcessor.Process(data, settings);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -65,6 +65,7 @@
|
||||||
<Compile Include="ConfigurationData\DataMapping\InputDataMapper.cs" />
|
<Compile Include="ConfigurationData\DataMapping\InputDataMapper.cs" />
|
||||||
<Compile Include="ConfigurationData\DataMapping\SecurityDataMapper.cs" />
|
<Compile Include="ConfigurationData\DataMapping\SecurityDataMapper.cs" />
|
||||||
<Compile Include="ConfigurationData\DataMapping\UserInterfaceDataMapper.cs" />
|
<Compile Include="ConfigurationData\DataMapping\UserInterfaceDataMapper.cs" />
|
||||||
|
<Compile Include="ConfigurationData\DataProcessor.cs" />
|
||||||
<Compile Include="ConfigurationData\Keys.cs" />
|
<Compile Include="ConfigurationData\Keys.cs" />
|
||||||
<Compile Include="ConfigurationData\DataValues.cs" />
|
<Compile Include="ConfigurationData\DataValues.cs" />
|
||||||
<Compile Include="Cryptography\CertificateStore.cs" />
|
<Compile Include="Cryptography\CertificateStore.cs" />
|
||||||
|
|
|
@ -51,6 +51,11 @@ namespace SafeExamBrowser.Settings.Browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FilterSettings Filter { get; set; }
|
public FilterSettings Filter { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The hash value of the raw settings data, used for integrity checks with server applications (see also <see cref="SendCustomHeaders"/>).
|
||||||
|
/// </summary>
|
||||||
|
public string HashValue { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The settings to be used for the main browser window.
|
/// The settings to be used for the main browser window.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -71,6 +76,11 @@ namespace SafeExamBrowser.Settings.Browser
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string QuitUrl { get; set; }
|
public string QuitUrl { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Determines whether custom request headers (e.g. for integrity checks) are sent with every HTTP request.
|
||||||
|
/// </summary>
|
||||||
|
public bool SendCustomHeaders { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The URL with which the main browser window will be loaded.
|
/// The URL with which the main browser window will be loaded.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
Loading…
Reference in a new issue