SEBWIN-221: Simplified configuration API for loading and client configuration.
This commit is contained in:
parent
4b634d8e99
commit
140abe789e
10 changed files with 107 additions and 54 deletions
|
@ -55,21 +55,20 @@ namespace SafeExamBrowser.Configuration
|
|||
this.programVersion = programVersion ?? string.Empty;
|
||||
}
|
||||
|
||||
public void ConfigureClientWith(Uri resource, EncryptionParameters encryption = null)
|
||||
public SaveStatus ConfigureClientWith(Uri resource, PasswordParameters password = null)
|
||||
{
|
||||
logger.Info($"Attempting to configure local client settings from '{resource}'...");
|
||||
|
||||
try
|
||||
{
|
||||
TryLoadData(resource, out Stream stream);
|
||||
TryLoadData(resource, out var stream);
|
||||
|
||||
using (stream)
|
||||
{
|
||||
// TODO:
|
||||
//TryParseData(stream, encryption, out _, out _, out var data);
|
||||
//HandleIdentityCertificates(data);
|
||||
TryParseData(stream, out var encryption, out var format, out var data, password);
|
||||
HandleIdentityCertificates(data);
|
||||
|
||||
// Save configuration data as local client config under %APPDATA%!
|
||||
// TODO: Encrypt and save configuration data as local client config under %APPDATA%!
|
||||
// -> New key will determine whether to use default password or current settings password!
|
||||
// -> "clientConfigEncryptUsingSettingsPassword"
|
||||
// -> Default settings password for local client configuration appears to be string.Empty -> passwords.SettingsPassword
|
||||
|
@ -77,10 +76,14 @@ namespace SafeExamBrowser.Configuration
|
|||
}
|
||||
|
||||
logger.Info($"Successfully configured local client settings with '{resource}'.");
|
||||
|
||||
return SaveStatus.Success;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
logger.Error($"Unexpected error while trying to configure local client settings '{resource}'!", e);
|
||||
|
||||
return SaveStatus.UnexpectedError;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,17 +169,15 @@ namespace SafeExamBrowser.Configuration
|
|||
dataResources.Add(dataResource);
|
||||
}
|
||||
|
||||
public LoadStatus TryLoadSettings(Uri resource, PasswordParameters password, out EncryptionParameters encryption, out Format format, out Settings settings)
|
||||
public LoadStatus TryLoadSettings(Uri resource, out Settings settings, PasswordParameters password = null)
|
||||
{
|
||||
logger.Info($"Attempting to load '{resource}'...");
|
||||
|
||||
encryption = default(EncryptionParameters);
|
||||
format = default(Format);
|
||||
settings = LoadDefaultSettings();
|
||||
|
||||
try
|
||||
{
|
||||
var status = TryLoadData(resource, out Stream stream);
|
||||
var status = TryLoadData(resource, out var stream);
|
||||
|
||||
using (stream)
|
||||
{
|
||||
|
@ -185,7 +186,7 @@ namespace SafeExamBrowser.Configuration
|
|||
return status;
|
||||
}
|
||||
|
||||
status = TryParseData(stream, password, out encryption, out format, out var data);
|
||||
status = TryParseData(stream, out _, out _, out var data, password);
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
{
|
||||
|
@ -219,7 +220,7 @@ namespace SafeExamBrowser.Configuration
|
|||
|
||||
foreach (var certificate in certificates)
|
||||
{
|
||||
var isIdentity = certificate.TryGetValue("type", out object t) && t is int type && type == IDENTITY_CERTIFICATE;
|
||||
var isIdentity = certificate.TryGetValue("type", out var o) && o is int type && type == IDENTITY_CERTIFICATE;
|
||||
var hasData = certificate.TryGetValue("certificateData", out value);
|
||||
|
||||
if (isIdentity && hasData && value is byte[] certificateData)
|
||||
|
@ -278,7 +279,7 @@ namespace SafeExamBrowser.Configuration
|
|||
return status;
|
||||
}
|
||||
|
||||
private LoadStatus TryParseData(Stream data, PasswordParameters password, out EncryptionParameters encryption, out Format format, out IDictionary<string, object> rawData)
|
||||
private LoadStatus TryParseData(Stream data, out EncryptionParameters encryption, out Format format, out IDictionary<string, object> rawData, PasswordParameters password = null)
|
||||
{
|
||||
var dataFormat = dataFormats.FirstOrDefault(f => f.CanParse(data));
|
||||
var status = LoadStatus.NotSupported;
|
||||
|
|
|
@ -60,7 +60,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return false;
|
||||
}
|
||||
|
||||
public ParseResult TryParse(Stream data, PasswordParameters password)
|
||||
public ParseResult TryParse(Stream data, PasswordParameters password = null)
|
||||
{
|
||||
var prefix = ParsePrefix(data);
|
||||
var success = TryDetermineFormat(prefix, out FormatType format);
|
||||
|
@ -94,29 +94,36 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return new ParseResult { Status = LoadStatus.InvalidData };
|
||||
}
|
||||
|
||||
private ParseResult ParsePasswordBlock(Stream data, FormatType format, PasswordParameters password)
|
||||
private ParseResult ParsePasswordBlock(Stream data, FormatType format, PasswordParameters password = null)
|
||||
{
|
||||
var encryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption)));
|
||||
var encryptionParams = new PasswordParameters();
|
||||
var result = new ParseResult();
|
||||
|
||||
if (format == FormatType.PasswordConfigureClient)
|
||||
if (password != null)
|
||||
{
|
||||
encryptionParams.Password = password.IsHash ? password.Password : hashAlgorithm.GenerateHashFor(password.Password);
|
||||
encryptionParams.IsHash = true;
|
||||
if (format == FormatType.PasswordConfigureClient)
|
||||
{
|
||||
encryptionParams.Password = password.IsHash ? password.Password : hashAlgorithm.GenerateHashFor(password.Password);
|
||||
encryptionParams.IsHash = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptionParams.Password = password.Password;
|
||||
encryptionParams.IsHash = password.IsHash;
|
||||
}
|
||||
|
||||
result.Status = encryption.Decrypt(data, encryptionParams.Password, out var decrypted);
|
||||
|
||||
if (result.Status == LoadStatus.Success)
|
||||
{
|
||||
result = ParsePlainDataBlock(decrypted);
|
||||
result.Encryption = encryptionParams;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
encryptionParams.Password = password.Password;
|
||||
encryptionParams.IsHash = password.IsHash;
|
||||
}
|
||||
|
||||
result.Status = encryption.Decrypt(data, encryptionParams.Password, out var decrypted);
|
||||
|
||||
if (result.Status == LoadStatus.Success)
|
||||
{
|
||||
result = ParsePlainDataBlock(decrypted);
|
||||
result.Encryption = encryptionParams;
|
||||
result.Status = LoadStatus.PasswordNeeded;
|
||||
}
|
||||
|
||||
return result;
|
||||
|
@ -134,7 +141,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return xmlFormat.TryParse(data);
|
||||
}
|
||||
|
||||
private ParseResult ParsePublicKeyHashBlock(Stream data, PasswordParameters password)
|
||||
private ParseResult ParsePublicKeyHashBlock(Stream data, PasswordParameters password = null)
|
||||
{
|
||||
var encryption = new PublicKeyHashEncryption(logger.CloneFor(nameof(PublicKeyHashEncryption)));
|
||||
var result = new ParseResult();
|
||||
|
@ -155,7 +162,7 @@ namespace SafeExamBrowser.Configuration.DataFormats
|
|||
return result;
|
||||
}
|
||||
|
||||
private ParseResult ParsePublicKeyHashWithSymmetricKeyBlock(Stream data, PasswordParameters password)
|
||||
private ParseResult ParsePublicKeyHashWithSymmetricKeyBlock(Stream data, PasswordParameters password = null)
|
||||
{
|
||||
var passwordEncryption = new PasswordEncryption(logger.CloneFor(nameof(PasswordEncryption)));
|
||||
var encryption = new PublicKeyHashWithSymmetricKeyEncryption(logger.CloneFor(nameof(PublicKeyHashWithSymmetricKeyEncryption)), passwordEncryption);
|
||||
|
|
|
@ -19,9 +19,9 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
public interface IConfigurationRepository
|
||||
{
|
||||
/// <summary>
|
||||
/// Saves the given resource as local client configuration.
|
||||
/// Attempts to save the given resource as local client configuration.
|
||||
/// </summary>
|
||||
void ConfigureClientWith(Uri resource, EncryptionParameters encryption = null);
|
||||
SaveStatus ConfigureClientWith(Uri resource, PasswordParameters password = null);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the global configuration information for the currently running application instance.
|
||||
|
@ -51,7 +51,7 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
/// <summary>
|
||||
/// Attempts to load settings from the specified resource.
|
||||
/// </summary>
|
||||
LoadStatus TryLoadSettings(Uri resource, PasswordParameters password, out EncryptionParameters encryption, out Format format, out Settings.Settings settings);
|
||||
LoadStatus TryLoadSettings(Uri resource, out Settings.Settings settings, PasswordParameters password = null);
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to save settings according to the specified parameters.
|
||||
|
|
|
@ -29,17 +29,17 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
NotSupported,
|
||||
|
||||
/// <summary>
|
||||
/// Indicates that a password is needed in order to decrypt the settings.
|
||||
/// Indicates that a password is needed in order to decrypt the configuration.
|
||||
/// </summary>
|
||||
PasswordNeeded,
|
||||
|
||||
/// <summary>
|
||||
/// The settings were loaded successfully.
|
||||
/// The configuration was loaded successfully.
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// An unexpected error occurred while trying to load the settings.
|
||||
/// An unexpected error occurred while trying to load the configuration.
|
||||
/// </summary>
|
||||
UnexpectedError
|
||||
}
|
||||
|
|
|
@ -13,6 +13,14 @@ namespace SafeExamBrowser.Contracts.Configuration
|
|||
/// </summary>
|
||||
public enum SaveStatus
|
||||
{
|
||||
/// <summary>
|
||||
/// The configuration was saved successfully.
|
||||
/// </summary>
|
||||
Success,
|
||||
|
||||
/// <summary>
|
||||
/// An unexpected error occurred while trying to save the configuration.
|
||||
/// </summary>
|
||||
UnexpectedError
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ namespace SafeExamBrowser.Contracts.I18n
|
|||
LogWindow_Title,
|
||||
MessageBox_ApplicationError,
|
||||
MessageBox_ApplicationErrorTitle,
|
||||
MessageBox_ClientConfigurationError,
|
||||
MessageBox_ClientConfigurationErrorTitle,
|
||||
MessageBox_ClientConfigurationQuestion,
|
||||
MessageBox_ClientConfigurationQuestionTitle,
|
||||
MessageBox_ConfigurationDownloadError,
|
||||
|
|
|
@ -12,6 +12,12 @@
|
|||
<Entry key="MessageBox_ApplicationErrorTitle">
|
||||
Application Error
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ClientConfigurationError">
|
||||
The local client configuration has failed! Please consult the application log for more information. The application will now shut down...
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ClientConfigurationErrorTitle">
|
||||
Client Configuration Error
|
||||
</Entry>
|
||||
<Entry key="MessageBox_ClientConfigurationQuestion">
|
||||
The client configuration has been saved and will be used when you start the application the next time. Do you want to quit for now?
|
||||
</Entry>
|
||||
|
|
|
@ -11,7 +11,6 @@ using System.IO;
|
|||
using SafeExamBrowser.Contracts.Communication.Data;
|
||||
using SafeExamBrowser.Contracts.Configuration;
|
||||
using SafeExamBrowser.Contracts.Configuration.Cryptography;
|
||||
using SafeExamBrowser.Contracts.Configuration.DataFormats;
|
||||
using SafeExamBrowser.Contracts.Configuration.Settings;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel;
|
||||
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
||||
|
@ -114,14 +113,14 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
private OperationResult LoadSettings(Uri uri)
|
||||
{
|
||||
var passwordParams = new PasswordParameters { Password = string.Empty, IsHash = true };
|
||||
var status = configuration.TryLoadSettings(uri, passwordParams, out var encryption, out var format, out var settings);
|
||||
var status = configuration.TryLoadSettings(uri, out var settings, passwordParams);
|
||||
|
||||
if (status == LoadStatus.PasswordNeeded && Context.Current?.Settings.AdminPasswordHash != null)
|
||||
{
|
||||
passwordParams.Password = Context.Current.Settings.AdminPasswordHash;
|
||||
passwordParams.IsHash = true;
|
||||
|
||||
status = configuration.TryLoadSettings(uri, passwordParams, out encryption, out format, out settings);
|
||||
status = configuration.TryLoadSettings(uri, out settings, passwordParams);
|
||||
}
|
||||
|
||||
for (int attempts = 0; attempts < 5 && status == LoadStatus.PasswordNeeded; attempts++)
|
||||
|
@ -138,15 +137,15 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Aborted;
|
||||
}
|
||||
|
||||
status = configuration.TryLoadSettings(uri, passwordParams, out encryption, out format, out settings);
|
||||
status = configuration.TryLoadSettings(uri, out settings, passwordParams);
|
||||
}
|
||||
|
||||
Context.Next.Settings = settings;
|
||||
|
||||
return HandleLoadResult(uri, settings, status, passwordParams, encryption, format);
|
||||
return HandleLoadResult(uri, settings, status, passwordParams);
|
||||
}
|
||||
|
||||
private OperationResult HandleLoadResult(Uri uri, Settings settings, LoadStatus status, PasswordParameters password, EncryptionParameters encryption, Format format)
|
||||
private OperationResult HandleLoadResult(Uri uri, Settings settings, LoadStatus status, PasswordParameters password)
|
||||
{
|
||||
if (status == LoadStatus.LoadWithBrowser)
|
||||
{
|
||||
|
@ -155,7 +154,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
if (status == LoadStatus.Success && settings.ConfigurationMode == ConfigurationMode.ConfigureClient)
|
||||
{
|
||||
return HandleClientConfiguration(uri, password, encryption, format);
|
||||
return HandleClientConfiguration(uri, password);
|
||||
}
|
||||
|
||||
if (status == LoadStatus.Success)
|
||||
|
@ -176,7 +175,7 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
return OperationResult.Success;
|
||||
}
|
||||
|
||||
private OperationResult HandleClientConfiguration(Uri resource, PasswordParameters password, EncryptionParameters encryption, Format format)
|
||||
private OperationResult HandleClientConfiguration(Uri resource, PasswordParameters password)
|
||||
{
|
||||
var isAppDataFile = Path.GetFullPath(resource.AbsolutePath).Equals(AppDataFile, StringComparison.OrdinalIgnoreCase);
|
||||
var isProgramDataFile = Path.GetFullPath(resource.AbsolutePath).Equals(ProgramDataFile, StringComparison.OrdinalIgnoreCase);
|
||||
|
@ -202,11 +201,19 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
logger.Info("Authentication is not required.");
|
||||
}
|
||||
|
||||
configuration.ConfigureClientWith(resource, encryption);
|
||||
var status = configuration.ConfigureClientWith(resource, password);
|
||||
|
||||
if (status != SaveStatus.Success)
|
||||
{
|
||||
logger.Error($"Client configuration failed with status '{status}'!");
|
||||
ActionRequired?.Invoke(new ClientConfigurationErrorMessageArgs());
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
|
||||
if (isFirstSession)
|
||||
{
|
||||
var result = HandleClientConfigurationSuccess();
|
||||
var result = HandleClientConfigurationOnStartup();
|
||||
|
||||
if (result != OperationResult.Success)
|
||||
{
|
||||
|
@ -270,21 +277,19 @@ namespace SafeExamBrowser.Runtime.Operations
|
|||
|
||||
return OperationResult.Success;
|
||||
}
|
||||
else
|
||||
{
|
||||
logger.Info("Authentication has failed!");
|
||||
ActionRequired?.Invoke(new InvalidPasswordMessageArgs());
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
logger.Info("Authentication has failed!");
|
||||
ActionRequired?.Invoke(new InvalidPasswordMessageArgs());
|
||||
|
||||
return OperationResult.Failed;
|
||||
}
|
||||
|
||||
private OperationResult HandleClientConfigurationSuccess()
|
||||
private OperationResult HandleClientConfigurationOnStartup()
|
||||
{
|
||||
var args = new ConfigurationCompletedEventArgs();
|
||||
|
||||
ActionRequired?.Invoke(args);
|
||||
logger.Info($"The user chose to {(args.AbortStartup ? "abort" : "continue")} after successful client configuration.");
|
||||
logger.Info($"The user chose to {(args.AbortStartup ? "abort" : "continue")} startup after successful client configuration.");
|
||||
|
||||
if (args.AbortStartup)
|
||||
{
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/*
|
||||
* Copyright (c) 2018 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 SafeExamBrowser.Contracts.I18n;
|
||||
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
|
||||
|
||||
namespace SafeExamBrowser.Runtime.Operations.Events
|
||||
{
|
||||
internal class ClientConfigurationErrorMessageArgs : MessageEventArgs
|
||||
{
|
||||
internal ClientConfigurationErrorMessageArgs()
|
||||
{
|
||||
Icon = MessageBoxIcon.Error;
|
||||
Message = TextKey.MessageBox_ClientConfigurationError;
|
||||
Title = TextKey.MessageBox_ClientConfigurationErrorTitle;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -89,6 +89,7 @@
|
|||
<Compile Include="Operations\ClientOperation.cs" />
|
||||
<Compile Include="Operations\ClientTerminationOperation.cs" />
|
||||
<Compile Include="Operations\ConfigurationOperation.cs" />
|
||||
<Compile Include="Operations\Events\ClientConfigurationErrorMessageArgs.cs" />
|
||||
<Compile Include="Operations\Events\ConfigurationCompletedEventArgs.cs" />
|
||||
<Compile Include="Operations\Events\InvalidDataMessageArgs.cs" />
|
||||
<Compile Include="Operations\Events\InvalidPasswordMessageArgs.cs" />
|
||||
|
|
Loading…
Reference in a new issue