From 68d487dd46a182b2f9244a131b022213aa2e7f4a Mon Sep 17 00:00:00 2001 From: dbuechel Date: Fri, 19 Jul 2019 10:07:45 +0200 Subject: [PATCH] SEBWIN-320: Implemented configuration reset functionality for reset utility. --- .../Lockdown/IFeatureConfiguration.cs | 5 + .../Lockdown/IFeatureConfigurationFactory.cs | 6 + .../SystemComponents/IUserInfo.cs | 5 + .../FeatureConfigurationStub.cs | 5 + .../FeatureConfigurationFactory.cs | 20 +++ .../FeatureConfiguration.cs | 1 + .../MachineHive/EaseOfAccessConfiguration.cs | 5 + .../NetworkOptionsConfiguration.cs | 5 + .../MachineHive/PowerOptionsConfiguration.cs | 5 + .../RemoteConnectionConfiguration.cs | 12 ++ .../MachineHive/SwitchUserConfiguration.cs | 5 + .../RegistryConfiguration.cs | 25 +++- .../UserHive/ChangePasswordConfiguration.cs | 5 + .../ChromeNotificationConfiguration.cs | 11 +- .../UserHive/LockWorkstationConfiguration.cs | 5 + .../UserHive/SignoutConfiguration.cs | 5 + .../UserHive/TaskManagerConfiguration.cs | 5 + .../UserHive/VmwareOverlayConfiguration.cs | 5 + .../WindowsUpdateConfiguration.cs | 5 + .../CompositionRoot.cs | 21 ++- .../Procedure/ProcedureContext.cs | 3 + .../Procedure/ProcedureStep.cs | 19 +++ .../Procedure/Reset.cs | 129 ++++++++++++++++++ .../Procedure/Restore.cs | 5 +- .../SafeExamBrowser.ResetUtility.csproj | 4 + SafeExamBrowser.Runtime/CompositionRoot.cs | 2 +- SafeExamBrowser.SystemComponents/UserInfo.cs | 84 ++++++++++++ 27 files changed, 390 insertions(+), 17 deletions(-) diff --git a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs index d2153b4d..6d935905 100644 --- a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs +++ b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfiguration.cs @@ -45,6 +45,11 @@ namespace SafeExamBrowser.Contracts.Lockdown /// void Initialize(); + /// + /// Resets the feature to its default configuration. Returns true if successful, otherwise false. + /// + bool Reset(); + /// /// Restores the feature to its previous configuration (i.e. before it was enabled or disabled). Returns true if successful, /// otherwise false. diff --git a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfigurationFactory.cs b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfigurationFactory.cs index 0e2dfb7c..92a2d009 100644 --- a/SafeExamBrowser.Contracts/Lockdown/IFeatureConfigurationFactory.cs +++ b/SafeExamBrowser.Contracts/Lockdown/IFeatureConfigurationFactory.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; namespace SafeExamBrowser.Contracts.Lockdown { @@ -15,6 +16,11 @@ namespace SafeExamBrowser.Contracts.Lockdown /// public interface IFeatureConfigurationFactory { + /// + /// Creates all feature configurations. + /// + IList CreateAll(Guid groupId, string sid, string userName); + /// /// Creates an to control the option to change the password of a user account via the security screen. /// diff --git a/SafeExamBrowser.Contracts/SystemComponents/IUserInfo.cs b/SafeExamBrowser.Contracts/SystemComponents/IUserInfo.cs index a19ffd88..25aad530 100644 --- a/SafeExamBrowser.Contracts/SystemComponents/IUserInfo.cs +++ b/SafeExamBrowser.Contracts/SystemComponents/IUserInfo.cs @@ -22,5 +22,10 @@ namespace SafeExamBrowser.Contracts.SystemComponents /// Retrieves the security identifier of the currently logged in user. /// string GetUserSid(); + + /// + /// Tries to retrieve the security identifier for the specified user name. Returns true if successful, otherwise false. + /// + bool TryGetSidForUser(string userName, out string sid); } } diff --git a/SafeExamBrowser.Lockdown.UnitTests/FeatureConfigurationStub.cs b/SafeExamBrowser.Lockdown.UnitTests/FeatureConfigurationStub.cs index 9339a815..bfc6f9bb 100644 --- a/SafeExamBrowser.Lockdown.UnitTests/FeatureConfigurationStub.cs +++ b/SafeExamBrowser.Lockdown.UnitTests/FeatureConfigurationStub.cs @@ -42,6 +42,11 @@ namespace SafeExamBrowser.Lockdown.UnitTests throw new NotImplementedException(); } + public bool Reset() + { + throw new NotImplementedException(); + } + public bool Restore() { throw new NotImplementedException(); diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurationFactory.cs b/SafeExamBrowser.Lockdown/FeatureConfigurationFactory.cs index 4c05055b..906e2f0f 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurationFactory.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurationFactory.cs @@ -7,6 +7,7 @@ */ using System; +using System.Collections.Generic; using SafeExamBrowser.Contracts.Lockdown; using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive; @@ -24,6 +25,25 @@ namespace SafeExamBrowser.Lockdown this.logger = logger; } + public IList CreateAll(Guid groupId, string sid, string userName) + { + return new List + { + CreateChangePasswordConfiguration(groupId, sid, userName), + CreateChromeNotificationConfiguration(groupId, sid, userName), + CreateEaseOfAccessConfiguration(groupId), + CreateLockWorkstationConfiguration(groupId, sid, userName), + CreateNetworkOptionsConfiguration(groupId), + CreatePowerOptionsConfiguration(groupId), + CreateRemoteConnectionConfiguration(groupId), + CreateSignoutConfiguration(groupId, sid, userName), + CreateSwitchUserConfiguration(groupId), + CreateTaskManagerConfiguration(groupId, sid, userName), + CreateVmwareOverlayConfiguration(groupId, sid, userName), + CreateWindowsUpdateConfiguration(groupId) + }; + } + public IFeatureConfiguration CreateChangePasswordConfiguration(Guid groupId, string sid, string userName) { return new ChangePasswordConfiguration(groupId, logger.CloneFor(nameof(ChangePasswordConfiguration)), sid, userName); diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs index c2b2ab3a..ae325b18 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/FeatureConfiguration.cs @@ -33,6 +33,7 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations public abstract bool EnableFeature(); public abstract FeatureConfigurationStatus GetStatus(); public abstract void Initialize(); + public abstract bool Reset(); public abstract bool Restore(); public override string ToString() diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/EaseOfAccessConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/EaseOfAccessConfiguration.cs index 376b4590..33c55541 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/EaseOfAccessConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/EaseOfAccessConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public EaseOfAccessConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/NetworkOptionsConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/NetworkOptionsConfiguration.cs index 8cdcc163..fdeda09e 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/NetworkOptionsConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/NetworkOptionsConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public NetworkOptionsConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/PowerOptionsConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/PowerOptionsConfiguration.cs index 042fcb6a..eb7d7652 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/PowerOptionsConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/PowerOptionsConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public PowerOptionsConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/RemoteConnectionConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/RemoteConnectionConfiguration.cs index df20dd4e..a9c87990 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/RemoteConnectionConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/RemoteConnectionConfiguration.cs @@ -12,6 +12,13 @@ using SafeExamBrowser.Contracts.Logging; namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations.MachineHive { + /// + /// Specifies whether Remote Desktop connections are enabled. + /// + /// See https://docs.microsoft.com/en-us/windows-hardware/customize/desktop/unattend/microsoft-windows-terminalservices-localsessionmanager-fdenytsconnections: + /// • 0 = Specifies that remote desktop connections are enabled. + /// • 1 = Specifies that remote desktop connections are denied. This is the default value. + /// [Serializable] internal class RemoteConnectionConfiguration : MachineHiveConfiguration { @@ -23,5 +30,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public RemoteConnectionConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return DisableFeature(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/SwitchUserConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/SwitchUserConfiguration.cs index 1a35010a..21aaa955 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/SwitchUserConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/MachineHive/SwitchUserConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public SwitchUserConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/RegistryConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/RegistryConfiguration.cs index bb5f4d65..eaaf9ed6 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/RegistryConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/RegistryConfiguration.cs @@ -30,6 +30,8 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations itemsToRestore = new List(); } + protected abstract bool IsHiveAvailable(RegistryDataItem item); + public override bool DisableFeature() { var success = true; @@ -66,7 +68,7 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations } else { - logger.Warn("Failed to enabled feature!"); + logger.Warn("Failed to enable feature!"); } return success; @@ -148,7 +150,26 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations return success; } - protected abstract bool IsHiveAvailable(RegistryDataItem item); + protected bool DeleteConfiguration() + { + var success = true; + + foreach (var item in Items) + { + success &= TryDelete(new RegistryDataItem { Key = item.Key, Value = item.Value }); + } + + if (success) + { + logger.Info("Successfully deleted feature configuration."); + } + else + { + logger.Warn("Failed to delete feature configuration!"); + } + + return success; + } private RegistryDataItem ReadItem(string key, string value) { diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChangePasswordConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChangePasswordConfiguration.cs index 68ef2b67..02579004 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChangePasswordConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChangePasswordConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public ChangePasswordConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChromeNotificationConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChromeNotificationConfiguration.cs index 35cee403..efb5e831 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChromeNotificationConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/ChromeNotificationConfiguration.cs @@ -16,9 +16,9 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. /// IMPORTANT: This registry configuration only has an effect after Chrome is restarted! /// /// See https://www.chromium.org/administrators/policy-list-3#DefaultNotificationsSetting: - /// • 1 = Allow sites to show desktop notifications - /// • 2 = Do not allow any site to show desktop notifications - /// • 3 = Ask every time a site wants to show desktop notifications + /// • 1 = Allow sites to show desktop notifications. + /// • 2 = Do not allow any site to show desktop notifications. + /// • 3 = Ask every time a site wants to show desktop notifications. /// [Serializable] internal class ChromeNotificationConfiguration : UserHiveConfiguration @@ -31,5 +31,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public ChromeNotificationConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/LockWorkstationConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/LockWorkstationConfiguration.cs index d597294b..bf8b00d2 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/LockWorkstationConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/LockWorkstationConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public LockWorkstationConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/SignoutConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/SignoutConfiguration.cs index ad027e19..10ae1afc 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/SignoutConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/SignoutConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public SignoutConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/TaskManagerConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/TaskManagerConfiguration.cs index b1ca350f..5f6556dc 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/TaskManagerConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/TaskManagerConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public TaskManagerConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return DeleteConfiguration(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/VmwareOverlayConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/VmwareOverlayConfiguration.cs index fd834736..b9d2946e 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/VmwareOverlayConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/RegistryConfigurations/UserHive/VmwareOverlayConfiguration.cs @@ -24,5 +24,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.RegistryConfigurations. public VmwareOverlayConfiguration(Guid groupId, ILogger logger, string sid, string userName) : base(groupId, logger, sid, userName) { } + + public override bool Reset() + { + return EnableFeature(); + } } } diff --git a/SafeExamBrowser.Lockdown/FeatureConfigurations/ServiceConfigurations/WindowsUpdateConfiguration.cs b/SafeExamBrowser.Lockdown/FeatureConfigurations/ServiceConfigurations/WindowsUpdateConfiguration.cs index b3eb2b5f..42ecb3a1 100644 --- a/SafeExamBrowser.Lockdown/FeatureConfigurations/ServiceConfigurations/WindowsUpdateConfiguration.cs +++ b/SafeExamBrowser.Lockdown/FeatureConfigurations/ServiceConfigurations/WindowsUpdateConfiguration.cs @@ -23,5 +23,10 @@ namespace SafeExamBrowser.Lockdown.FeatureConfigurations.ServiceConfigurations public WindowsUpdateConfiguration(Guid groupId, ILogger logger) : base(groupId, logger) { } + + public override bool Reset() + { + return EnableFeature(); + } } } diff --git a/SafeExamBrowser.ResetUtility/CompositionRoot.cs b/SafeExamBrowser.ResetUtility/CompositionRoot.cs index 6dd88e43..bf1ae37d 100644 --- a/SafeExamBrowser.ResetUtility/CompositionRoot.cs +++ b/SafeExamBrowser.ResetUtility/CompositionRoot.cs @@ -14,11 +14,13 @@ using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Lockdown; using SafeExamBrowser.Logging; using SafeExamBrowser.ResetUtility.Procedure; +using SafeExamBrowser.SystemComponents; namespace SafeExamBrowser.ResetUtility { internal class CompositionRoot { + private ProcedureContext context; private ILogger logger; internal ProcedureStep InitialStep { get; private set; } @@ -26,14 +28,8 @@ namespace SafeExamBrowser.ResetUtility internal void BuildObjectGraph() { - var context = new ProcedureContext(); - InitializeLogging(); - - context.CreateBackup = CreateBackup; - context.Logger = logger; - context.MainMenu = BuildMainMenu(context); - context.Update = new SystemConfigurationUpdate(new ModuleLogger(logger, nameof(SystemConfigurationUpdate))); + InitializeContext(); InitialStep = new Initialization(context); NativeMethods = new NativeMethods(); @@ -68,6 +64,17 @@ namespace SafeExamBrowser.ResetUtility return new FeatureConfigurationBackup(filePath, new ModuleLogger(logger, nameof(FeatureConfigurationBackup))); } + private void InitializeContext() + { + context = new ProcedureContext(); + context.ConfigurationFactory = new FeatureConfigurationFactory(new ModuleLogger(logger, nameof(FeatureConfigurationFactory))); + context.CreateBackup = CreateBackup; + context.Logger = logger; + context.MainMenu = BuildMainMenu(context); + context.Update = new SystemConfigurationUpdate(new ModuleLogger(logger, nameof(SystemConfigurationUpdate))); + context.UserInfo = new UserInfo(new ModuleLogger(logger, nameof(UserInfo))); + } + private void InitializeLogging() { var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), nameof(SafeExamBrowser)); diff --git a/SafeExamBrowser.ResetUtility/Procedure/ProcedureContext.cs b/SafeExamBrowser.ResetUtility/Procedure/ProcedureContext.cs index 4d8ba85d..ae19b45f 100644 --- a/SafeExamBrowser.ResetUtility/Procedure/ProcedureContext.cs +++ b/SafeExamBrowser.ResetUtility/Procedure/ProcedureContext.cs @@ -10,14 +10,17 @@ using System; using System.Collections.Generic; using SafeExamBrowser.Contracts.Lockdown; using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Contracts.SystemComponents; namespace SafeExamBrowser.ResetUtility.Procedure { internal class ProcedureContext { + internal IFeatureConfigurationFactory ConfigurationFactory { get; set; } internal Func CreateBackup { get; set; } internal ILogger Logger { get; set; } internal IList MainMenu { get; set; } internal ISystemConfigurationUpdate Update { get; set; } + internal IUserInfo UserInfo { get; set; } } } diff --git a/SafeExamBrowser.ResetUtility/Procedure/ProcedureStep.cs b/SafeExamBrowser.ResetUtility/Procedure/ProcedureStep.cs index 320af50f..30d4b51a 100644 --- a/SafeExamBrowser.ResetUtility/Procedure/ProcedureStep.cs +++ b/SafeExamBrowser.ResetUtility/Procedure/ProcedureStep.cs @@ -57,6 +57,25 @@ namespace SafeExamBrowser.ResetUtility.Procedure Console.CursorVisible = false; } + protected void ClearLine(int top) + { + Console.SetCursorPosition(0, top); + Console.WriteLine(new String(' ', Console.BufferWidth)); + Console.SetCursorPosition(0, top); + } + + protected string ReadLine() + { + Console.Write("> "); + Console.CursorVisible = true; + + var input = Console.ReadLine(); + + Console.CursorVisible = false; + + return input; + } + protected void ShowError(string message) { Console.ForegroundColor = ConsoleColor.Red; diff --git a/SafeExamBrowser.ResetUtility/Procedure/Reset.cs b/SafeExamBrowser.ResetUtility/Procedure/Reset.cs index 3300b6f2..9a03ae22 100644 --- a/SafeExamBrowser.ResetUtility/Procedure/Reset.cs +++ b/SafeExamBrowser.ResetUtility/Procedure/Reset.cs @@ -6,6 +6,11 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ +using System; +using System.Collections.Generic; +using System.Linq; +using SafeExamBrowser.Contracts.Lockdown; + namespace SafeExamBrowser.ResetUtility.Procedure { internal class Reset : ProcedureStep @@ -16,6 +21,15 @@ namespace SafeExamBrowser.ResetUtility.Procedure internal override ProcedureStepResult Execute() { + InitializeConsole(); + + var success = TryGetUserInfo(out var userName, out var sid); + + if (success) + { + ResetAll(userName, sid); + } + return ProcedureStepResult.Continue; } @@ -23,5 +37,120 @@ namespace SafeExamBrowser.ResetUtility.Procedure { return new MainMenu(Context); } + + private bool TryGetUserInfo(out string userName, out string sid) + { + Console.ForegroundColor = ConsoleColor.DarkYellow; + Console.WriteLine("IMPORTANT: Some configuration values are user specific. In order to reset these values, the user specified below needs to be logged in!"); + Console.ForegroundColor = ForegroundColor; + Console.WriteLine(); + Console.WriteLine("Please enter the name of the user for which to reset all configuration values:"); + + userName = ReadLine(); + + StartProgressAnimation(); + var success = Context.UserInfo.TryGetSidForUser(userName, out sid); + StopProgressAnimation(); + + while (!success) + { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Could not find user '{userName}'!"); + Console.ForegroundColor = ForegroundColor; + + var tryAgain = new MenuOption { IsSelected = true, Text = "Try again" }; + var mainMenu = new MenuOption { Text = "Return to main menu" }; + + ShowMenu(new List { tryAgain, mainMenu }); + + if (mainMenu.IsSelected) + { + break; + } + + ClearLine(Console.CursorTop - 1); + ClearLine(Console.CursorTop - 1); + ClearLine(Console.CursorTop - 1); + ClearLine(Console.CursorTop - 1); + + userName = ReadLine(); + success = Context.UserInfo.TryGetSidForUser(userName, out sid); + } + + return success; + } + + private void ResetAll(string userName, string sid) + { + var configurations = Context.ConfigurationFactory.CreateAll(Guid.NewGuid(), sid, userName); + var failed = new List(); + + Logger.Info($"Attempting to reset all configuration values for user '{userName}' with SID '{sid}'..."); + Console.WriteLine(); + Console.WriteLine("Initiating reset procedure..."); + + foreach (var configuration in configurations) + { + var success = configuration.Reset(); + + if (!success) + { + 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() + { + Console.WriteLine(); + Console.WriteLine("Performing system configuration update, please wait..."); + StartProgressAnimation(); + Context.Update.Execute(); + StopProgressAnimation(); + Console.WriteLine("Update completed."); + } + + private void HandleFailure(IList configurations) + { + Logger.Warn($"Failed to reset {configurations.Count} items!"); + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine($"Failed to reset {configurations.Count} items!"); + + foreach (var configuration in configurations) + { + Console.WriteLine($" - {configuration.GetType().Name}"); + } + + Console.ForegroundColor = ForegroundColor; + Console.WriteLine(); + Console.WriteLine("Please consult the application log for more information."); + } + + private void HandleSuccess() + { + Logger.Info("Successfully reset all changes!"); + Console.WriteLine(); + Console.ForegroundColor = ConsoleColor.DarkGreen; + Console.WriteLine("Successfully reset all changes!"); + Console.ForegroundColor = ForegroundColor; + } } } diff --git a/SafeExamBrowser.ResetUtility/Procedure/Restore.cs b/SafeExamBrowser.ResetUtility/Procedure/Restore.cs index 4adabc01..b472dc93 100644 --- a/SafeExamBrowser.ResetUtility/Procedure/Restore.cs +++ b/SafeExamBrowser.ResetUtility/Procedure/Restore.cs @@ -6,7 +6,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - using System; using System.Collections.Generic; using System.IO; @@ -104,13 +103,11 @@ namespace SafeExamBrowser.ResetUtility.Procedure private void PerformUpdate() { - Logger.Info("Starting system configuration update..."); Console.WriteLine(); Console.WriteLine("Performing system configuration update, please wait..."); StartProgressAnimation(); Context.Update.Execute(); StopProgressAnimation(); - Logger.Info("Update completed."); Console.WriteLine("Update completed."); } @@ -160,7 +157,7 @@ namespace SafeExamBrowser.ResetUtility.Procedure next = new Reset(Context); } - Logger.Info($"The user chose {(yes.IsSelected ? "" : "not ")}to perform a reset for now."); + Logger.Info($"The user chose {(yes.IsSelected ? "" : "not ")}to perform a reset."); } } } diff --git a/SafeExamBrowser.ResetUtility/SafeExamBrowser.ResetUtility.csproj b/SafeExamBrowser.ResetUtility/SafeExamBrowser.ResetUtility.csproj index b12c3df3..ba2c5f64 100644 --- a/SafeExamBrowser.ResetUtility/SafeExamBrowser.ResetUtility.csproj +++ b/SafeExamBrowser.ResetUtility/SafeExamBrowser.ResetUtility.csproj @@ -92,6 +92,10 @@ {e107026c-2011-4552-a7d8-3a0d37881df6} SafeExamBrowser.Logging + + {acee2ef1-14d2-4b52-8994-5c053055bb51} + SafeExamBrowser.SystemComponents + diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index c690932d..fb6f6c76 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -71,7 +71,7 @@ namespace SafeExamBrowser.Runtime var serviceProxy = new ServiceProxy(appConfig.ServiceAddress, new ProxyObjectFactory(), ModuleLogger(nameof(ServiceProxy)), Interlocutor.Runtime); var sessionContext = new SessionContext(); var uiFactory = new UserInterfaceFactory(text); - var userInfo = new UserInfo(); + var userInfo = new UserInfo(ModuleLogger(nameof(UserInfo))); var bootstrapOperations = new Queue(); var sessionOperations = new Queue(); diff --git a/SafeExamBrowser.SystemComponents/UserInfo.cs b/SafeExamBrowser.SystemComponents/UserInfo.cs index b16e80a3..2e5897ea 100644 --- a/SafeExamBrowser.SystemComponents/UserInfo.cs +++ b/SafeExamBrowser.SystemComponents/UserInfo.cs @@ -7,13 +7,25 @@ */ using System; +using System.Diagnostics; using System.Security.Principal; +using System.Text.RegularExpressions; +using SafeExamBrowser.Contracts.Logging; using SafeExamBrowser.Contracts.SystemComponents; namespace SafeExamBrowser.SystemComponents { public class UserInfo : IUserInfo { + private const string SID_REGEX_PATTERN = @"S-\d(-\d+)+"; + + private ILogger logger; + + public UserInfo(ILogger logger) + { + this.logger = logger; + } + public string GetUserName() { return Environment.UserName; @@ -23,5 +35,77 @@ namespace SafeExamBrowser.SystemComponents { return WindowsIdentity.GetCurrent().User.Value; } + + public bool TryGetSidForUser(string userName, out string sid) + { + var strategies = new Func[] { NtAccount, Wmi }; + var success = false; + + sid = default(string); + + foreach (var strategy in strategies) + { + try + { + sid = strategy.Invoke(userName); + + if (IsValid(sid)) + { + logger.Info($"Found SID '{sid}' via '{strategy.Method.Name}' for user name '{userName}'!"); + success = true; + + break; + } + + logger.Warn($"Retrieved invalid SID '{sid}' via '{strategy.Method.Name}' for user name '{userName}'!"); + } + catch (Exception e) + { + logger.Error($"Failed to get SID via '{strategy.Method.Name}' for user name '{userName}'!", e); + } + } + + if (!success) + { + logger.Error($"All attempts to retrieve SID for user name '{userName}' failed!"); + } + + return success; + } + + private string NtAccount(string userName) + { + var account = new NTAccount(userName); + + if (account.IsValidTargetType(typeof(SecurityIdentifier))) + { + return account.Translate(typeof(SecurityIdentifier)).Value; + } + + return null; + } + + private string Wmi(string userName) + { + var process = new Process(); + + process.StartInfo.FileName = "cmd.exe"; + process.StartInfo.Arguments = string.Format("/c \"wmic useraccount where name='{0}' get sid\"", userName); + process.StartInfo.UseShellExecute = false; + process.StartInfo.RedirectStandardOutput = true; + process.StartInfo.CreateNoWindow = true; + process.Start(); + process.WaitForExit(5000); + + var output = process.StandardOutput.ReadToEnd(); + var match = Regex.Match(output, SID_REGEX_PATTERN); + + return match.Success ? match.Value : null; + } + + private bool IsValid(string sid) + { + return !String.IsNullOrWhiteSpace(sid) && Regex.IsMatch(sid, $"^{SID_REGEX_PATTERN}$"); + } } }