240 lines
7.1 KiB
C#
240 lines
7.1 KiB
C#
/*
|
|
* 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 System;
|
|
using System.IO;
|
|
using SafeExamBrowser.Contracts.Communication.Data;
|
|
using SafeExamBrowser.Contracts.Configuration;
|
|
using SafeExamBrowser.Contracts.Configuration.Settings;
|
|
using SafeExamBrowser.Contracts.Core.OperationModel;
|
|
using SafeExamBrowser.Contracts.Core.OperationModel.Events;
|
|
using SafeExamBrowser.Contracts.I18n;
|
|
using SafeExamBrowser.Contracts.Logging;
|
|
using SafeExamBrowser.Runtime.Operations.Events;
|
|
|
|
namespace SafeExamBrowser.Runtime.Operations
|
|
{
|
|
internal class ConfigurationOperation : SessionOperation
|
|
{
|
|
private string[] commandLineArgs;
|
|
private IConfigurationRepository configuration;
|
|
private ILogger logger;
|
|
|
|
public override event ActionRequiredEventHandler ActionRequired;
|
|
public override event StatusChangedEventHandler StatusChanged;
|
|
|
|
public ConfigurationOperation(
|
|
string[] commandLineArgs,
|
|
IConfigurationRepository configuration,
|
|
ILogger logger,
|
|
SessionContext sessionContext) : base(sessionContext)
|
|
{
|
|
this.commandLineArgs = commandLineArgs;
|
|
this.logger = logger;
|
|
this.configuration = configuration;
|
|
}
|
|
|
|
public override OperationResult Perform()
|
|
{
|
|
logger.Info("Initializing application configuration...");
|
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
|
|
|
|
var isValidUri = TryInitializeSettingsUri(out Uri uri);
|
|
|
|
if (isValidUri)
|
|
{
|
|
var result = LoadSettings(uri);
|
|
|
|
HandleClientConfiguration(ref result, uri);
|
|
LogOperationResult(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
logger.Info("No valid configuration resource specified nor found in PROGRAMDATA or APPDATA - loading default settings...");
|
|
Context.Next.Settings = configuration.LoadDefaultSettings();
|
|
|
|
return OperationResult.Success;
|
|
}
|
|
|
|
public override OperationResult Repeat()
|
|
{
|
|
logger.Info("Initializing new application configuration...");
|
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeConfiguration);
|
|
|
|
var isValidUri = TryValidateSettingsUri(Context.ReconfigurationFilePath, out Uri uri);
|
|
|
|
if (isValidUri)
|
|
{
|
|
var result = LoadSettings(uri);
|
|
|
|
LogOperationResult(result);
|
|
|
|
return result;
|
|
}
|
|
|
|
logger.Warn($"The resource specified for reconfiguration does not exist or is not valid!");
|
|
|
|
return OperationResult.Failed;
|
|
}
|
|
|
|
public override OperationResult Revert()
|
|
{
|
|
return OperationResult.Success;
|
|
}
|
|
|
|
private OperationResult LoadSettings(Uri uri)
|
|
{
|
|
var adminPassword = default(string);
|
|
var settingsPassword = default(string);
|
|
var settings = default(Settings);
|
|
var status = default(LoadStatus);
|
|
|
|
for (int adminAttempts = 0, settingsAttempts = 0; adminAttempts < 5 && settingsAttempts < 5;)
|
|
{
|
|
status = configuration.TryLoadSettings(uri, out settings, adminPassword, settingsPassword);
|
|
|
|
if (status == LoadStatus.AdminPasswordNeeded || status == LoadStatus.SettingsPasswordNeeded)
|
|
{
|
|
var result = TryGetPassword(status);
|
|
|
|
if (!result.Success)
|
|
{
|
|
return OperationResult.Aborted;
|
|
}
|
|
|
|
adminAttempts += status == LoadStatus.AdminPasswordNeeded ? 1 : 0;
|
|
adminPassword = status == LoadStatus.AdminPasswordNeeded ? result.Password : adminPassword;
|
|
settingsAttempts += status == LoadStatus.SettingsPasswordNeeded ? 1 : 0;
|
|
settingsPassword = status == LoadStatus.SettingsPasswordNeeded ? result.Password : settingsPassword;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (status == LoadStatus.Success)
|
|
{
|
|
Context.Next.Settings = settings;
|
|
}
|
|
else
|
|
{
|
|
ShowFailureMessage(status, uri);
|
|
}
|
|
|
|
return status == LoadStatus.Success ? OperationResult.Success : OperationResult.Failed;
|
|
}
|
|
|
|
private void ShowFailureMessage(LoadStatus status, Uri uri)
|
|
{
|
|
switch (status)
|
|
{
|
|
case LoadStatus.InvalidData:
|
|
ActionRequired?.Invoke(new InvalidDataMessageArgs(uri.ToString()));
|
|
break;
|
|
case LoadStatus.NotSupported:
|
|
ActionRequired?.Invoke(new NotSupportedMessageArgs(uri.ToString()));
|
|
break;
|
|
case LoadStatus.UnexpectedError:
|
|
ActionRequired?.Invoke(new UnexpectedErrorMessageArgs(uri.ToString()));
|
|
break;
|
|
}
|
|
}
|
|
|
|
private PasswordRequiredEventArgs TryGetPassword(LoadStatus status)
|
|
{
|
|
var purpose = status == LoadStatus.AdminPasswordNeeded ? PasswordRequestPurpose.Administrator : PasswordRequestPurpose.Settings;
|
|
var args = new PasswordRequiredEventArgs { Purpose = purpose };
|
|
|
|
ActionRequired?.Invoke(args);
|
|
|
|
return args;
|
|
}
|
|
|
|
private bool TryInitializeSettingsUri(out Uri uri)
|
|
{
|
|
var path = string.Empty;
|
|
var isValidUri = false;
|
|
var programDataSettings = Path.Combine(Context.Next.AppConfig.ProgramDataFolder, Context.Next.AppConfig.DefaultSettingsFileName);
|
|
var appDataSettings = Path.Combine(Context.Next.AppConfig.AppDataFolder, Context.Next.AppConfig.DefaultSettingsFileName);
|
|
|
|
uri = null;
|
|
|
|
if (commandLineArgs?.Length > 1)
|
|
{
|
|
path = commandLineArgs[1];
|
|
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
|
|
logger.Info($"Found command-line argument for configuration resource: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
|
|
}
|
|
|
|
if (!isValidUri && File.Exists(programDataSettings))
|
|
{
|
|
path = programDataSettings;
|
|
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
|
|
logger.Info($"Found configuration file in PROGRAMDATA: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
|
|
}
|
|
|
|
if (!isValidUri && File.Exists(appDataSettings))
|
|
{
|
|
path = appDataSettings;
|
|
isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
|
|
logger.Info($"Found configuration file in APPDATA: '{path}', the URI is {(isValidUri ? "valid" : "invalid")}.");
|
|
}
|
|
|
|
return isValidUri;
|
|
}
|
|
|
|
private bool TryValidateSettingsUri(string path, out Uri uri)
|
|
{
|
|
var isValidUri = Uri.TryCreate(path, UriKind.Absolute, out uri);
|
|
|
|
isValidUri &= uri != null && uri.IsFile;
|
|
isValidUri &= File.Exists(path);
|
|
|
|
return isValidUri;
|
|
}
|
|
|
|
private void HandleClientConfiguration(ref OperationResult result, Uri uri)
|
|
{
|
|
var configureMode = Context.Next.Settings?.ConfigurationMode == ConfigurationMode.ConfigureClient;
|
|
var loadWithBrowser = Context.Next.Settings?.Browser.StartUrl == uri.AbsoluteUri;
|
|
var successful = result == OperationResult.Success;
|
|
|
|
if (successful && configureMode && !loadWithBrowser)
|
|
{
|
|
var args = new ConfigurationCompletedEventArgs();
|
|
|
|
ActionRequired?.Invoke(args);
|
|
|
|
logger.Info($"The user chose to {(args.AbortStartup ? "abort" : "continue")} after successful client configuration.");
|
|
|
|
if (args.AbortStartup)
|
|
{
|
|
result = OperationResult.Aborted;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void LogOperationResult(OperationResult result)
|
|
{
|
|
switch (result)
|
|
{
|
|
case OperationResult.Aborted:
|
|
logger.Info("The configuration was aborted by the user.");
|
|
break;
|
|
case OperationResult.Failed:
|
|
logger.Warn("The configuration has failed!");
|
|
break;
|
|
case OperationResult.Success:
|
|
logger.Info("The configuration was successful.");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|