diff --git a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs index cd8e8be3..cecac4cb 100644 --- a/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs +++ b/SafeExamBrowser.Client.UnitTests/Operations/ShellOperationTests.cs @@ -104,6 +104,7 @@ namespace SafeExamBrowser.Client.UnitTests.Operations context.Activators.Add(terminationActivator.Object); context.Settings.ActionCenter.EnableActionCenter = true; context.Settings.Keyboard.AllowAltTab = true; + context.Settings.Security.AllowTermination = true; sut.Perform(); @@ -218,6 +219,32 @@ namespace SafeExamBrowser.Client.UnitTests.Operations taskbar.VerifySet(t => t.ShowClock = false, Times.Once); } + [TestMethod] + public void Perform_MustInitializeQuitButton() + { + context.Settings.ActionCenter.EnableActionCenter = true; + context.Settings.Taskbar.EnableTaskbar = true; + context.Settings.Security.AllowTermination = false; + + sut.Perform(); + + actionCenter.VerifySet(a => a.ShowQuitButton = false, Times.Once); + taskbar.VerifySet(t => t.ShowQuitButton = false, Times.Once); + actionCenter.VerifySet(a => a.ShowQuitButton = true, Times.Never); + taskbar.VerifySet(t => t.ShowQuitButton = true, Times.Never); + + actionCenter.Reset(); + taskbar.Reset(); + context.Settings.Security.AllowTermination = true; + + sut.Perform(); + + actionCenter.VerifySet(a => a.ShowQuitButton = false, Times.Never); + taskbar.VerifySet(t => t.ShowQuitButton = false, Times.Never); + actionCenter.VerifySet(a => a.ShowQuitButton = true, Times.Once); + taskbar.VerifySet(t => t.ShowQuitButton = true, Times.Once); + } + [TestMethod] public void Perform_MustInitializeNotifications() { @@ -346,6 +373,19 @@ namespace SafeExamBrowser.Client.UnitTests.Operations taskbar.VerifyNoOtherCalls(); } + [TestMethod] + public void Perform_MustNotInitializeTerminationActivatorIfNotEnabled() + { + var terminationActivator = new Mock(); + + context.Activators.Add(terminationActivator.Object); + context.Settings.Security.AllowTermination = false; + + sut.Perform(); + + terminationActivator.Verify(a => a.Start(), Times.Never); + } + [TestMethod] public void Revert_MustTerminateActivators() { diff --git a/SafeExamBrowser.Client/Operations/ShellOperation.cs b/SafeExamBrowser.Client/Operations/ShellOperation.cs index f8ce1196..939a1d4d 100644 --- a/SafeExamBrowser.Client/Operations/ShellOperation.cs +++ b/SafeExamBrowser.Client/Operations/ShellOperation.cs @@ -120,7 +120,7 @@ namespace SafeExamBrowser.Client.Operations taskViewActivator.Start(); } - if (activator is ITerminationActivator terminationActivator) + if (Context.Settings.Security.AllowTermination && activator is ITerminationActivator terminationActivator) { terminationActivator.Start(); } @@ -142,6 +142,7 @@ namespace SafeExamBrowser.Client.Operations InitializeKeyboardLayoutForActionCenter(); InitializeWirelessNetworkForActionCenter(); InitializePowerSupplyForActionCenter(); + InitializeQuitButtonForActionCenter(); } else { @@ -164,6 +165,7 @@ namespace SafeExamBrowser.Client.Operations InitializeAudioForTaskbar(); InitializeKeyboardLayoutForTaskbar(); InitializeClockForTaskbar(); + InitializeQuitButtonForTaskbar(); } else { @@ -302,6 +304,16 @@ namespace SafeExamBrowser.Client.Operations } } + private void InitializeQuitButtonForActionCenter() + { + actionCenter.ShowQuitButton = Context.Settings.Security.AllowTermination; + } + + private void InitializeQuitButtonForTaskbar() + { + taskbar.ShowQuitButton = Context.Settings.Security.AllowTermination; + } + private void InitializeWirelessNetworkForActionCenter() { if (Context.Settings.ActionCenter.ShowWirelessNetwork) diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/SecurityDataMapper.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/SecurityDataMapper.cs index daea8f5d..13ca5588 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/SecurityDataMapper.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataMapping/SecurityDataMapper.cs @@ -22,6 +22,9 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping case Keys.ConfigurationFile.AdminPasswordHash: MapAdminPasswordHash(settings, value); break; + case Keys.Security.AllowTermination: + MapAllowTermination(settings, value); + break; case Keys.Security.AllowVirtualMachine: MapVirtualMachinePolicy(settings, value); break; @@ -48,6 +51,14 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping } } + private void MapAllowTermination(AppSettings settings, object value) + { + if (value is bool allow) + { + settings.Security.AllowTermination = allow; + } + } + private void MapApplicationLogAccess(IDictionary rawData, AppSettings settings) { var hasValue = rawData.TryGetValue(Keys.Security.AllowApplicationLog, out var value); @@ -112,7 +123,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData.DataMapping { if (value is bool allow) { - settings.Security.VirtualMachinePolicy = allow ? VirtualMachinePolicy.Allow : VirtualMachinePolicy.Deny ; + settings.Security.VirtualMachinePolicy = allow ? VirtualMachinePolicy.Allow : VirtualMachinePolicy.Deny; } } } diff --git a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs index c02af071..bd22e713 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/DataValues.cs @@ -167,6 +167,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData settings.Mouse.AllowRightButton = true; settings.Security.AllowApplicationLogAccess = false; + settings.Security.AllowTermination = true; settings.Security.AllowReconfiguration = false; settings.Security.KioskMode = KioskMode.CreateNewDesktop; settings.Security.VirtualMachinePolicy = VirtualMachinePolicy.Deny; diff --git a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs index 75e636f5..646175d7 100644 --- a/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs +++ b/SafeExamBrowser.Configuration/ConfigurationData/Keys.cs @@ -207,6 +207,7 @@ namespace SafeExamBrowser.Configuration.ConfigurationData internal static class Security { internal const string AllowApplicationLog = "allowApplicationLog"; + internal const string AllowTermination = "allowQuit"; internal const string AllowVirtualMachine = "allowVirtualMachine"; internal const string KioskModeCreateNewDesktop = "createNewDesktop"; internal const string KioskModeDisableExplorerShell = "killExplorerShell"; diff --git a/SafeExamBrowser.Settings/Security/SecuritySettings.cs b/SafeExamBrowser.Settings/Security/SecuritySettings.cs index 5057867c..6d5f9276 100644 --- a/SafeExamBrowser.Settings/Security/SecuritySettings.cs +++ b/SafeExamBrowser.Settings/Security/SecuritySettings.cs @@ -26,6 +26,11 @@ namespace SafeExamBrowser.Settings.Security /// public bool AllowApplicationLogAccess { get; set; } + /// + /// Determines whether the user may initiate the termination of SEB. This setting does not affect automated mechanisms like a quit URL. + /// + public bool AllowTermination { get; set; } + /// /// Determines whether the user may reconfigure the application. /// diff --git a/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs b/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs index e60e7fa2..a197aa48 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Shell/IActionCenter.cs @@ -21,6 +21,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell /// bool ShowClock { set; } + /// + /// Controls the visibility of the quit button. + /// + bool ShowQuitButton { set; } + /// /// Event fired when the user clicked the quit button. /// diff --git a/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs b/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs index 337c61bd..f90051fd 100644 --- a/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs +++ b/SafeExamBrowser.UserInterface.Contracts/Shell/ITaskbar.cs @@ -21,6 +21,11 @@ namespace SafeExamBrowser.UserInterface.Contracts.Shell /// bool ShowClock { set; } + /// + /// Controls the visibility of the quit button. + /// + bool ShowQuitButton { set; } + /// /// Event fired when the user clicked the quit button in the taskbar. /// diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/ActionCenter.xaml.cs index 8c6adfa2..145cdc7a 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/ActionCenter.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/ActionCenter.xaml.cs @@ -24,6 +24,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } } + public bool ShowQuitButton + { + set { Dispatcher.Invoke(() => QuitButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } + } + public event QuitButtonClickedEventHandler QuitButtonClicked; internal ActionCenter() diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml b/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml index 2d45ddb3..80199e63 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml @@ -21,7 +21,7 @@ - + diff --git a/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml.cs index 65129f3a..727cfb72 100644 --- a/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml.cs +++ b/SafeExamBrowser.UserInterface.Desktop/Windows/Taskbar.xaml.cs @@ -26,6 +26,11 @@ namespace SafeExamBrowser.UserInterface.Desktop.Windows set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } } + public bool ShowQuitButton + { + set { Dispatcher.Invoke(() => QuitButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } + } + public event QuitButtonClickedEventHandler QuitButtonClicked; internal Taskbar(ILogger logger) diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/ActionCenter.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/ActionCenter.xaml.cs index 2b887036..b5e1642b 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/ActionCenter.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/ActionCenter.xaml.cs @@ -24,6 +24,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } } + public bool ShowQuitButton + { + set { Dispatcher.Invoke(() => QuitButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } + } + public event QuitButtonClickedEventHandler QuitButtonClicked; internal ActionCenter() diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml b/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml index 6d1a736d..2ce32ed0 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml @@ -21,7 +21,7 @@ - + diff --git a/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml.cs b/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml.cs index 63e1d9dd..a7b61b84 100644 --- a/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml.cs +++ b/SafeExamBrowser.UserInterface.Mobile/Windows/Taskbar.xaml.cs @@ -26,6 +26,11 @@ namespace SafeExamBrowser.UserInterface.Mobile.Windows set { Dispatcher.Invoke(() => Clock.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } } + public bool ShowQuitButton + { + set { Dispatcher.Invoke(() => QuitButton.Visibility = value ? Visibility.Visible : Visibility.Collapsed); } + } + public event QuitButtonClickedEventHandler QuitButtonClicked; internal Taskbar(ILogger logger)