SEBWIN-320: Implemented restore functionality for reset utility.

This commit is contained in:
dbuechel 2019-07-17 16:17:20 +02:00
parent 85c88da6cc
commit 86a3e9ce3c
11 changed files with 297 additions and 93 deletions

View file

@ -7,8 +7,11 @@
*/
using System;
using System.Collections.Generic;
using System.IO;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.Lockdown;
using SafeExamBrowser.Logging;
using SafeExamBrowser.ResetUtility.Procedure;
@ -23,12 +26,14 @@ namespace SafeExamBrowser.ResetUtility
internal void BuildObjectGraph()
{
var context = new Context();
InitializeLogging();
var context = new Context
{
Logger = logger,
};
context.CreateBackup = CreateBackup;
context.Logger = logger;
context.MainMenu = BuildMainMenu(context);
context.Update = new SystemConfigurationUpdate(new ModuleLogger(logger, nameof(SystemConfigurationUpdate)));
InitialStep = new Initialization(context);
NativeMethods = new NativeMethods();
@ -46,6 +51,23 @@ namespace SafeExamBrowser.ResetUtility
logger?.Log($"# Application terminated at {DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff")}");
}
private IList<MainMenuOption> BuildMainMenu(Context context)
{
return new List<MainMenuOption>
{
new MainMenuOption { IsSelected = true, NextStep = new Restore(context), Result = ProcedureStepResult.Continue, Text = "Restore system configuration via backup mechanism" },
new MainMenuOption { NextStep = new Reset(context), Result = ProcedureStepResult.Continue, Text = "Reset system configuration to default values" },
new MainMenuOption { NextStep = new Procedure.Version(context), Result = ProcedureStepResult.Continue, Text = "Show version information" },
new MainMenuOption { NextStep = new Log(context), Result = ProcedureStepResult.Continue, Text = "Show application log" },
new MainMenuOption { Result = ProcedureStepResult.Terminate, Text = "Exit" }
};
}
private IFeatureConfigurationBackup CreateBackup(string filePath)
{
return new FeatureConfigurationBackup(filePath, new ModuleLogger(logger, nameof(FeatureConfigurationBackup)));
}
private void InitializeLogging()
{
var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser));

View file

@ -6,12 +6,19 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using SafeExamBrowser.Contracts.Lockdown;
using SafeExamBrowser.Contracts.Logging;
using SafeExamBrowser.ResetUtility.Procedure;
namespace SafeExamBrowser.ResetUtility
{
internal class Context
{
internal Func<string, IFeatureConfigurationBackup> CreateBackup { get; set; }
internal ILogger Logger { get; set; }
internal IList<MainMenuOption> MainMenu { get; set; }
internal ISystemConfigurationUpdate Update { get; set; }
}
}

View file

@ -12,9 +12,9 @@ using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.ResetUtility.Procedure
{
internal class ShowLog : ProcedureStep
internal class Log : ProcedureStep
{
public ShowLog(Context context) : base(context)
public Log(Context context) : base(context)
{
}

View file

@ -6,98 +6,27 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.Linq;
namespace SafeExamBrowser.ResetUtility.Procedure
{
internal class MainMenu : ProcedureStep
{
private IList<Option> options;
public MainMenu(Context context) : base(context)
{
options = new List<Option>
{
new Option { IsSelected = true, NextStep = new Restore(Context), Result = ProcedureStepResult.Continue, Text = "Restore system configuration via backup mechanism" },
new Option { NextStep = new Reset(Context), Result = ProcedureStepResult.Continue, Text = "Reset system configuration to default values" },
new Option { NextStep = new ShowVersion(Context), Result = ProcedureStepResult.Continue, Text = "Show version information" },
new Option { NextStep = new ShowLog(Context), Result = ProcedureStepResult.Continue, Text = "Show application log" },
new Option { Result = ProcedureStepResult.Terminate, Text = "Exit" }
};
}
internal override ProcedureStepResult Execute()
{
PrintMenu();
InitializeConsole();
ShowMenu(Context.MainMenu.Cast<MenuOption>().ToList(), true);
for (var key = Console.ReadKey(true).Key; key != ConsoleKey.Enter; key = Console.ReadKey(true).Key)
{
if (key == ConsoleKey.UpArrow || key == ConsoleKey.DownArrow)
{
SelectNextOption(key);
PrintMenu();
}
}
return options.First(o => o.IsSelected).Result;
return Context.MainMenu.First(o => o.IsSelected).Result;
}
internal override ProcedureStep GetNextStep()
{
return options.First(o => o.IsSelected).NextStep;
}
private void PrintMenu()
{
InitializeConsole();
Console.WriteLine("Please choose one of the following options:");
Console.WriteLine();
foreach (var option in options)
{
Console.WriteLine(option.ToString());
}
Console.WriteLine();
Console.WriteLine("Use the up/down arrow keys and enter to navigate the menu.");
}
private void SelectNextOption(ConsoleKey key)
{
var current = options.First(o => o.IsSelected);
var currentIndex = options.IndexOf(current);
var nextIndex = default(int);
if (key == ConsoleKey.UpArrow)
{
nextIndex = --currentIndex < 0 ? options.Count - 1 : currentIndex;
}
if (key == ConsoleKey.DownArrow)
{
nextIndex = ++currentIndex == options.Count ? 0 : currentIndex;
}
var next = options.ElementAt(nextIndex);
current.IsSelected = false;
next.IsSelected = true;
}
private class Option
{
public bool IsSelected { get; set; }
public ProcedureStep NextStep { get; set; }
public ProcedureStepResult Result { get; set; }
public string Text { get; set; }
public override string ToString()
{
return $"[{(IsSelected ? "x" : " ")}] {Text}";
}
return Context.MainMenu.First(o => o.IsSelected).NextStep;
}
}
}

View file

@ -0,0 +1,16 @@
/*
* Copyright (c) 2019 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.ResetUtility.Procedure
{
internal class MainMenuOption : MenuOption
{
internal ProcedureStep NextStep { get; set; }
internal ProcedureStepResult Result { get; set; }
}
}

View file

@ -0,0 +1,21 @@
/*
* Copyright (c) 2019 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.ResetUtility.Procedure
{
internal class MenuOption
{
internal bool IsSelected { get; set; }
internal string Text { get; set; }
public override string ToString()
{
return $"[{(IsSelected ? "x" : " ")}] {Text}";
}
}
}

View file

@ -7,6 +7,8 @@
*/
using System;
using System.Collections.Generic;
using System.Linq;
using SafeExamBrowser.Contracts.Logging;
namespace SafeExamBrowser.ResetUtility.Procedure
@ -29,9 +31,11 @@ namespace SafeExamBrowser.ResetUtility.Procedure
protected void InitializeConsole()
{
var title = "SEB Reset Utility";
var height = Console.LargestWindowHeight > 40 ? 40 : Console.LargestWindowHeight;
var width = Console.LargestWindowWidth > 160 ? 160 : Console.LargestWindowWidth;
Console.SetBufferSize(Console.WindowWidth, Console.WindowHeight);
Console.SetWindowSize(Console.BufferWidth, Console.BufferHeight);
Console.SetBufferSize(width, height);
Console.SetWindowSize(width, height);
Console.BackgroundColor = BackgroundColor;
Console.ForegroundColor = ForegroundColor;
Console.Clear();
@ -55,5 +59,76 @@ namespace SafeExamBrowser.ResetUtility.Procedure
Console.ForegroundColor = ForegroundColor;
Console.ReadKey();
}
protected void ShowMenu(IList<MenuOption> options, bool showInstructions = false)
{
var left = Console.CursorLeft;
var top = Console.CursorTop;
PrintMenu(options, left, top, showInstructions);
for (var key = Console.ReadKey(true).Key; key != ConsoleKey.Enter; key = Console.ReadKey(true).Key)
{
if (key == ConsoleKey.UpArrow || key == ConsoleKey.DownArrow)
{
SelectNextOption(key, options);
PrintMenu(options, left, top, showInstructions);
}
}
}
protected void ShowProgress(int current, int total)
{
var scale = Console.BufferWidth - 8.0;
var progress = Math.Floor(current * scale / total);
var remaining = Math.Ceiling((total - current) * scale / total);
Console.SetCursorPosition(0, Console.CursorTop);
Console.Write($"[{new String('■', (int) progress)}{new String('─', (int) remaining)}] {current * 100 / total}%");
}
private void PrintMenu(IList<MenuOption> options, int left, int top, bool showInstructions)
{
Console.SetCursorPosition(left, top);
if (showInstructions)
{
Console.WriteLine("Please choose one of the following options:");
Console.WriteLine();
}
foreach (var option in options)
{
Console.WriteLine(option.ToString());
}
if (showInstructions)
{
Console.WriteLine();
Console.WriteLine("Use the up/down arrow keys and enter to navigate the menu.");
}
}
private void SelectNextOption(ConsoleKey key, IList<MenuOption> options)
{
var current = options.First(o => o.IsSelected);
var currentIndex = options.IndexOf(current);
var nextIndex = default(int);
if (key == ConsoleKey.UpArrow)
{
nextIndex = --currentIndex < 0 ? options.Count - 1 : currentIndex;
}
if (key == ConsoleKey.DownArrow)
{
nextIndex = ++currentIndex == options.Count ? 0 : currentIndex;
}
var next = options.ElementAt(nextIndex);
current.IsSelected = false;
next.IsSelected = true;
}
}
}

View file

@ -6,8 +6,6 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
namespace SafeExamBrowser.ResetUtility.Procedure
{
internal class Reset : ProcedureStep
@ -18,12 +16,12 @@ namespace SafeExamBrowser.ResetUtility.Procedure
internal override ProcedureStepResult Execute()
{
throw new NotImplementedException();
return ProcedureStepResult.Continue;
}
internal override ProcedureStep GetNextStep()
{
throw new NotImplementedException();
return new MainMenu(Context);
}
}
}

View file

@ -6,24 +6,158 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Lockdown;
namespace SafeExamBrowser.ResetUtility.Procedure
{
internal class Restore : ProcedureStep
{
private ProcedureStep next;
public Restore(Context context) : base(context)
{
next = new MainMenu(Context);
}
internal override ProcedureStepResult Execute()
{
throw new NotImplementedException();
var filePath = $@"config\systemprofile\AppData\Local\{nameof(SafeExamBrowser)}\{AppConfig.BACKUP_FILE_NAME}";
var x86FilePath = Environment.ExpandEnvironmentVariables($@"%WINDIR%\system32\{filePath}");
var x64FilePath = Environment.ExpandEnvironmentVariables($@"%WINDIR%\SysWOW64\{filePath}");
InitializeConsole();
Logger.Info("Searching backup file...");
Logger.Debug($"x86 path => {x86FilePath}");
Logger.Debug($"x64 path => {x64FilePath}");
Console.WriteLine("Searching backup file...");
if (File.Exists(x86FilePath))
{
RestoreBackup(x86FilePath);
}
else if (File.Exists(x64FilePath))
{
RestoreBackup(x64FilePath);
}
else
{
HandleNoBackupFile();
}
return ProcedureStepResult.Continue;
}
internal override ProcedureStep GetNextStep()
{
throw new NotImplementedException();
return next;
}
private void RestoreBackup(string filePath)
{
var backup = Context.CreateBackup(filePath);
var configurations = backup.GetAllConfigurations();
var failed = new List<IFeatureConfiguration>();
Logger.Info($"Found backup file '{filePath}' with {configurations.Count} items. Initiating restore procedure...");
Console.WriteLine($"Found backup file with {configurations.Count} items.");
Console.WriteLine("Initiating restore procedure...");
foreach (var configuration in configurations)
{
var success = configuration.Restore();
if (success)
{
backup.Delete(configuration);
}
else
{
failed.Add(configuration);
}
ShowProgress(configurations.IndexOf(configuration) + 1, configurations.Count);
}
PerformUpdate();
if (failed.Any())
{
HandleFailure(failed);
}
else
{
HandleSuccess();
}
Console.WriteLine();
Console.WriteLine("Press any key to return to the main menu.");
Console.ReadKey();
}
private void PerformUpdate()
{
Logger.Info("Starting system configuration update...");
Console.WriteLine();
Console.WriteLine("Performing system configuration update, please wait...");
Context.Update.Execute();
Logger.Info("Update completed.");
Console.WriteLine("Update completed.");
}
private void HandleFailure(IList<IFeatureConfiguration> configurations)
{
Logger.Warn($"Failed to restore {configurations.Count} items!");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"Failed to restore {configurations.Count} items!");
foreach (var configuration in configurations)
{
Console.WriteLine($" - {configuration.GetType().Name}");
}
Console.ForegroundColor = ForegroundColor;
Console.WriteLine();
Console.WriteLine("Some configuration values may be user specific. In order to restore these values, the user who used SEB needs to be logged in.");
}
private void HandleSuccess()
{
Logger.Info("Successfully restored all changes!");
Console.WriteLine();
Console.ForegroundColor = ConsoleColor.DarkGreen;
Console.WriteLine("Successfully restored all changes!");
Console.ForegroundColor = ForegroundColor;
}
private void HandleNoBackupFile()
{
var yes = new MenuOption { IsSelected = true, Text = "Yes" };
var no = new MenuOption { Text = "No" };
Logger.Warn("Could not find any backup file!");
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Could not find any backup file!");
Console.ForegroundColor = ForegroundColor;
Console.WriteLine();
Console.WriteLine("Would you like to reset all configuration values possibly changed by SEB?");
ShowMenu(new List<MenuOption> { yes, no });
if (yes.IsSelected)
{
next = new Reset(Context);
}
Logger.Info($"The user chose {(yes.IsSelected ? "" : "not ")}to perform a reset for now.");
}
}
}

View file

@ -11,9 +11,9 @@ using System.Reflection;
namespace SafeExamBrowser.ResetUtility.Procedure
{
internal class ShowVersion : ProcedureStep
internal class Version : ProcedureStep
{
public ShowVersion(Context context) : base(context)
public Version(Context context) : base(context)
{
}

View file

@ -65,12 +65,14 @@
<Compile Include="NativeMethods.cs" />
<Compile Include="Procedure\Initialization.cs" />
<Compile Include="Procedure\MainMenu.cs" />
<Compile Include="Procedure\MainMenuOption.cs" />
<Compile Include="Procedure\MenuOption.cs" />
<Compile Include="Procedure\ProcedureStep.cs" />
<Compile Include="Procedure\ProcedureStepResult.cs" />
<Compile Include="Procedure\Reset.cs" />
<Compile Include="Procedure\Restore.cs" />
<Compile Include="Procedure\ShowLog.cs" />
<Compile Include="Procedure\ShowVersion.cs" />
<Compile Include="Procedure\Log.cs" />
<Compile Include="Procedure\Version.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>