diff --git a/SafeExamBrowser.Contracts/Communication/Data/AuthenticationResponse.cs b/SafeExamBrowser.Contracts/Communication/Data/AuthenticationResponse.cs
index 9c2c0d4f..81434a44 100644
--- a/SafeExamBrowser.Contracts/Communication/Data/AuthenticationResponse.cs
+++ b/SafeExamBrowser.Contracts/Communication/Data/AuthenticationResponse.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Data
{
///
- /// The response to be used to reply to an authentication request (see ).
+ /// The response to be used to reply to an authentication request (see ).
///
[Serializable]
public class AuthenticationResponse : Response
diff --git a/SafeExamBrowser.Contracts/Communication/Data/ConfigurationResponse.cs b/SafeExamBrowser.Contracts/Communication/Data/ConfigurationResponse.cs
index e9b4a29d..5b68fd3d 100644
--- a/SafeExamBrowser.Contracts/Communication/Data/ConfigurationResponse.cs
+++ b/SafeExamBrowser.Contracts/Communication/Data/ConfigurationResponse.cs
@@ -12,7 +12,7 @@ using SafeExamBrowser.Contracts.Configuration;
namespace SafeExamBrowser.Contracts.Communication.Data
{
///
- /// The response to be used to reply to a configuration request (see ).
+ /// The response to be used to reply to a configuration request (see ).
///
[Serializable]
public class ConfigurationResponse : Response
diff --git a/SafeExamBrowser.Contracts/Communication/Data/DisconnectionResponse.cs b/SafeExamBrowser.Contracts/Communication/Data/DisconnectionResponse.cs
index fcca0e79..ea218f71 100644
--- a/SafeExamBrowser.Contracts/Communication/Data/DisconnectionResponse.cs
+++ b/SafeExamBrowser.Contracts/Communication/Data/DisconnectionResponse.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Data
{
///
- /// The response transmitted to a
+ /// The response transmitted to a
///
[Serializable]
public class DisconnectionResponse : Response
diff --git a/SafeExamBrowser.Contracts/Communication/Data/PasswordReplyMessage.cs b/SafeExamBrowser.Contracts/Communication/Data/PasswordReplyMessage.cs
new file mode 100644
index 00000000..99aaf52c
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Data/PasswordReplyMessage.cs
@@ -0,0 +1,41 @@
+/*
+ * 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;
+
+namespace SafeExamBrowser.Contracts.Communication.Data
+{
+ ///
+ /// The reply to a .
+ ///
+ [Serializable]
+ public class PasswordReplyMessage : Message
+ {
+ ///
+ /// The password entered by the user, or null if the user interaction was unsuccessful.
+ ///
+ public string Password { get; private set; }
+
+ ///
+ /// The unique identifier for the password request.
+ ///
+ public Guid RequestId { get; private set; }
+
+ ///
+ /// Determines whether the user interaction was successful or not.
+ ///
+ public bool Success { get; private set; }
+
+ public PasswordReplyMessage(string password, Guid requestId, bool success)
+ {
+ Password = password;
+ RequestId = requestId;
+ Success = success;
+ }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestMessage.cs b/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestMessage.cs
new file mode 100644
index 00000000..8d04707b
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestMessage.cs
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+namespace SafeExamBrowser.Contracts.Communication.Data
+{
+ ///
+ /// This message is transmitted to the client to request a password input by the user.
+ ///
+ [Serializable]
+ public class PasswordRequestMessage : Message
+ {
+ ///
+ /// The purpose of the password request.
+ ///
+ public PasswordRequestPurpose Purpose { get; private set; }
+
+ ///
+ /// The unique identifier for the password request.
+ ///
+ public Guid RequestId { get; private set; }
+
+ public PasswordRequestMessage(PasswordRequestPurpose purpose, Guid requestId)
+ {
+ Purpose = purpose;
+ RequestId = requestId;
+ }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestPurpose.cs b/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestPurpose.cs
new file mode 100644
index 00000000..9cd49f82
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Data/PasswordRequestPurpose.cs
@@ -0,0 +1,28 @@
+/*
+ * 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/.
+ */
+
+namespace SafeExamBrowser.Contracts.Communication.Data
+{
+ ///
+ /// Defines all possible reasons for a .
+ ///
+ public enum PasswordRequestPurpose
+ {
+ Undefined = 0,
+
+ ///
+ /// The password is to be used as administrator password for an application configuration.
+ ///
+ Administrator,
+
+ ///
+ /// The password is to be used as settings password for an application configuration.
+ ///
+ Settings
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Data/ReconfigurationResponse.cs b/SafeExamBrowser.Contracts/Communication/Data/ReconfigurationResponse.cs
index 7fa5f22a..65671f70 100644
--- a/SafeExamBrowser.Contracts/Communication/Data/ReconfigurationResponse.cs
+++ b/SafeExamBrowser.Contracts/Communication/Data/ReconfigurationResponse.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Data
{
///
- /// The response to a .
+ /// The response to a .
///
[Serializable]
public class ReconfigurationResponse : Response
diff --git a/SafeExamBrowser.Contracts/Communication/Data/Response.cs b/SafeExamBrowser.Contracts/Communication/Data/Response.cs
index e338b4dc..20459fc8 100644
--- a/SafeExamBrowser.Contracts/Communication/Data/Response.cs
+++ b/SafeExamBrowser.Contracts/Communication/Data/Response.cs
@@ -11,7 +11,7 @@ using System;
namespace SafeExamBrowser.Contracts.Communication.Data
{
///
- /// The base class for respones, from which a response must inherit in order to be sent to an interlocutor as reply to .
+ /// The base class for respones, from which a response must inherit in order to be sent to an interlocutor as reply to .
///
[Serializable]
public abstract class Response
diff --git a/SafeExamBrowser.Contracts/Communication/Events/PasswordEventArgs.cs b/SafeExamBrowser.Contracts/Communication/Events/PasswordEventArgs.cs
new file mode 100644
index 00000000..742c0520
--- /dev/null
+++ b/SafeExamBrowser.Contracts/Communication/Events/PasswordEventArgs.cs
@@ -0,0 +1,33 @@
+/*
+ * 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;
+
+namespace SafeExamBrowser.Contracts.Communication.Events
+{
+ ///
+ /// The event arguments used for the password input event fired by the .
+ ///
+ public class PasswordEventArgs : CommunicationEventArgs
+ {
+ ///
+ /// The password entered by the user, or null if not available.
+ ///
+ public string Password { get; set; }
+
+ ///
+ /// Identifies the password request.
+ ///
+ public Guid RequestId { get; set; }
+
+ ///
+ /// Indicates whether the password has been successfully entered by the user.
+ ///
+ public bool Success { get; set; }
+ }
+}
diff --git a/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs b/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
index 070a6d1b..a2839f48 100644
--- a/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
+++ b/SafeExamBrowser.Contracts/Communication/Hosts/IRuntimeHost.cs
@@ -31,6 +31,11 @@ namespace SafeExamBrowser.Contracts.Communication.Hosts
///
event CommunicationEventHandler ClientReady;
+ ///
+ /// Event fired when the client transmitted a password entered by the user.
+ ///
+ event CommunicationEventHandler PasswordReceived;
+
///
/// Event fired when the client requested a reconfiguration of the application.
///
diff --git a/SafeExamBrowser.Contracts/Communication/Proxies/IClientProxy.cs b/SafeExamBrowser.Contracts/Communication/Proxies/IClientProxy.cs
index 40324cfe..d62abb93 100644
--- a/SafeExamBrowser.Contracts/Communication/Proxies/IClientProxy.cs
+++ b/SafeExamBrowser.Contracts/Communication/Proxies/IClientProxy.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using SafeExamBrowser.Contracts.Communication.Data;
namespace SafeExamBrowser.Contracts.Communication.Proxies
@@ -26,5 +27,11 @@ namespace SafeExamBrowser.Contracts.Communication.Proxies
///
/// If the communication failed.
AuthenticationResponse RequestAuthentication();
+
+ ///
+ /// Requests the client to render a password dialog and subsequently return the interaction result as separate message.
+ ///
+ /// If the communication failed.
+ void RequestPassword(PasswordRequestPurpose purpose, Guid requestId);
}
}
diff --git a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
index 4fd13f9c..0e8f7f94 100644
--- a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
+++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs
@@ -51,7 +51,7 @@ namespace SafeExamBrowser.Contracts.Configuration
/// Attempts to load settings from the specified resource, using the optional passwords. Returns a
/// indicating the result of the operation.
///
- LoadStatus LoadSettings(Uri resource, string settingsPassword = null, string adminPassword = null);
+ LoadStatus LoadSettings(Uri resource, string adminPassword = null, string settingsPassword = null);
///
/// Loads the default settings.
diff --git a/SafeExamBrowser.Contracts/I18n/TextKey.cs b/SafeExamBrowser.Contracts/I18n/TextKey.cs
index 95d25833..1685a828 100644
--- a/SafeExamBrowser.Contracts/I18n/TextKey.cs
+++ b/SafeExamBrowser.Contracts/I18n/TextKey.cs
@@ -44,6 +44,10 @@ namespace SafeExamBrowser.Contracts.I18n
MessageBox_StartupErrorTitle,
Notification_AboutTooltip,
Notification_LogTooltip,
+ PasswordDialog_AdminPasswordRequired,
+ PasswordDialog_AdminPasswordRequiredTitle,
+ PasswordDialog_SettingsPasswordRequired,
+ PasswordDialog_SettingsPasswordRequiredTitle,
ProgressIndicator_CloseRuntimeConnection,
ProgressIndicator_EmptyClipboard,
ProgressIndicator_FinalizeServiceSession,
diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
index a5465de4..ca417339 100644
--- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
+++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj
@@ -60,8 +60,12 @@
+
+
+
+
@@ -139,6 +143,8 @@
+
+
diff --git a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs
index 16a6fe7c..1555e823 100644
--- a/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs
+++ b/SafeExamBrowser.Contracts/UserInterface/IUserInterfaceFactory.cs
@@ -37,6 +37,11 @@ namespace SafeExamBrowser.Contracts.UserInterface
///
IBrowserWindow CreateBrowserWindow(IBrowserControl control, BrowserSettings settings);
+ ///
+ /// Creates a system control which allows to change the keyboard layout of the computer.
+ ///
+ ISystemKeyboardLayoutControl CreateKeyboardLayoutControl();
+
///
/// Creates a new log window which runs on its own thread.
///
@@ -48,9 +53,9 @@ namespace SafeExamBrowser.Contracts.UserInterface
INotificationButton CreateNotification(INotificationInfo info);
///
- /// Creates a system control which allows to change the keyboard layout of the computer.
+ /// Creates a password dialog with the given message and title.
///
- ISystemKeyboardLayoutControl CreateKeyboardLayoutControl();
+ IPasswordDialog CreatePasswordDialog(string message, string title);
///
/// Creates a system control displaying the power supply status of the computer.
diff --git a/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialog.cs b/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialog.cs
new file mode 100644
index 00000000..baf02339
--- /dev/null
+++ b/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialog.cs
@@ -0,0 +1,21 @@
+/*
+ * 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/.
+ */
+
+namespace SafeExamBrowser.Contracts.UserInterface.Windows
+{
+ ///
+ /// Defines the functionality of a password dialog.
+ ///
+ public interface IPasswordDialog : IWindow
+ {
+ ///
+ /// Shows the dialog as topmost window. If a parent window is specified, the dialog is rendered modally for the given parent.
+ ///
+ IPasswordDialogResult Show(IWindow parent = null);
+ }
+}
diff --git a/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialogResult.cs b/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialogResult.cs
new file mode 100644
index 00000000..83757917
--- /dev/null
+++ b/SafeExamBrowser.Contracts/UserInterface/Windows/IPasswordDialogResult.cs
@@ -0,0 +1,26 @@
+/*
+ * 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/.
+ */
+
+namespace SafeExamBrowser.Contracts.UserInterface.Windows
+{
+ ///
+ /// Defines the user interaction result of an .
+ ///
+ public interface IPasswordDialogResult
+ {
+ ///
+ /// The password entered by the user, or null if the interaction was unsuccessful.
+ ///
+ string Password { get; }
+
+ ///
+ /// Indicates whether the user confirmed the dialog or not.
+ ///
+ bool Success { get; }
+ }
+}
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.cs b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostStub.cs
similarity index 93%
rename from SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.cs
rename to SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostStub.cs
index b0fb5a11..f0c1165b 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostImpl.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostStub.cs
@@ -14,14 +14,14 @@ using SafeExamBrowser.Core.Communication.Hosts;
namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts
{
- internal class BaseHostImpl : BaseHost
+ internal class BaseHostStub : BaseHost
{
public Func OnConnectStub { get; set; }
public Action OnDisconnectStub { get; set; }
public Func OnReceiveStub { get; set; }
public Func OnReceiveSimpleMessageStub { get; set; }
- public BaseHostImpl(string address, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger)
+ public BaseHostStub(string address, IHostObjectFactory factory, ILogger logger) : base(address, factory, logger)
{
}
diff --git a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs
index 50198e23..68d65007 100644
--- a/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs
+++ b/SafeExamBrowser.Core.UnitTests/Communication/Hosts/BaseHostTests.cs
@@ -24,7 +24,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts
private Mock hostObject;
private Mock hostObjectFactory;
private Mock logger;
- private BaseHostImpl sut;
+ private BaseHostStub sut;
[TestInitialize]
public void Initialize()
@@ -35,7 +35,7 @@ namespace SafeExamBrowser.Core.UnitTests.Communication.Hosts
hostObjectFactory.Setup(f => f.CreateObject(It.IsAny(), It.IsAny())).Returns(hostObject.Object);
- sut = new BaseHostImpl("net.pipe://some/address/here", hostObjectFactory.Object, logger.Object);
+ sut = new BaseHostStub("net.pipe://some/address/here", hostObjectFactory.Object, logger.Object);
}
[TestMethod]
diff --git a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
index 382a44ab..bab8b185 100644
--- a/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
+++ b/SafeExamBrowser.Core.UnitTests/SafeExamBrowser.Core.UnitTests.csproj
@@ -83,7 +83,7 @@
-
+
diff --git a/SafeExamBrowser.Core/Communication/Proxies/ClientProxy.cs b/SafeExamBrowser.Core/Communication/Proxies/ClientProxy.cs
index 71f243d2..8d4fe460 100644
--- a/SafeExamBrowser.Core/Communication/Proxies/ClientProxy.cs
+++ b/SafeExamBrowser.Core/Communication/Proxies/ClientProxy.cs
@@ -6,6 +6,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+using System;
using System.ServiceModel;
using SafeExamBrowser.Contracts.Communication.Data;
using SafeExamBrowser.Contracts.Communication.Proxies;
@@ -43,5 +44,11 @@ namespace SafeExamBrowser.Core.Communication.Proxies
throw new CommunicationException($"Did not receive authentication response! Received: {ToString(response)}.");
}
+
+ public void RequestPassword(PasswordRequestPurpose purpose, Guid requestId)
+ {
+ // TODO
+ throw new NotImplementedException();
+ }
}
}
diff --git a/SafeExamBrowser.Core/I18n/Text.xml b/SafeExamBrowser.Core/I18n/Text.xml
index 2e71af35..5374a81e 100644
--- a/SafeExamBrowser.Core/I18n/Text.xml
+++ b/SafeExamBrowser.Core/I18n/Text.xml
@@ -84,6 +84,18 @@
Application Log
+
+ Please enter the administrator password for the application configuration:
+
+
+ Administrator Password Required
+
+
+ Please enter the settings password for the application configuration:
+
+
+ Settings Password Required
+
Closing runtime connection
diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
index 6c3cdbe3..6083e357 100644
--- a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
+++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/ConfigurationOperationTests.cs
@@ -11,11 +11,14 @@ using System.IO;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using SafeExamBrowser.Contracts.Behaviour.OperationModel;
+using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
using SafeExamBrowser.Contracts.Logging;
+using SafeExamBrowser.Contracts.UserInterface;
using SafeExamBrowser.Contracts.UserInterface.MessageBox;
+using SafeExamBrowser.Contracts.UserInterface.Windows;
using SafeExamBrowser.Runtime.Behaviour.Operations;
namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
@@ -23,12 +26,15 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestClass]
public class ConfigurationOperationTests
{
+ private RuntimeInfo info;
private Mock logger;
private Mock messageBox;
- private RuntimeInfo info;
+ private Mock passwordDialog;
private Mock repository;
+ private Mock runtimeHost;
private Settings settings;
private Mock text;
+ private Mock uiFactory;
private ConfigurationOperation sut;
[TestInitialize]
@@ -37,42 +43,24 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
info = new RuntimeInfo();
logger = new Mock();
messageBox = new Mock();
+ passwordDialog = new Mock();
repository = new Mock();
+ runtimeHost = new Mock();
settings = new Settings();
text = new Mock();
+ uiFactory = new Mock();
info.AppDataFolder = @"C:\Not\Really\AppData";
info.DefaultSettingsFileName = "SettingsDummy.txt";
info.ProgramDataFolder = @"C:\Not\Really\ProgramData";
- }
- [TestMethod]
- public void MustNotFailWithoutCommandLineArgs()
- {
- repository.Setup(r => r.LoadDefaultSettings());
-
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
- sut.Perform();
-
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, new string[] { });
- sut.Perform();
-
- repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
- }
-
- [TestMethod]
- public void MustNotFailWithInvalidUri()
- {
- var path = @"an/invalid\path.'*%yolo/()";
-
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, new[] { "blubb.exe", path });
- sut.Perform();
+ uiFactory.Setup(f => f.CreatePasswordDialog(It.IsAny(), It.IsAny())).Returns(passwordDialog.Object);
}
[TestMethod]
public void MustUseCommandLineArgumentAs1stPrio()
{
- var path = @"http://www.safeexambrowser.org/whatever.seb";
+ var url = @"http://www.safeexambrowser.org/whatever.seb";
var location = Path.GetDirectoryName(GetType().Assembly.Location);
info.ProgramDataFolder = location;
@@ -81,10 +69,10 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
repository.SetupGet(r => r.CurrentSettings).Returns(settings);
repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, new[] { "blubb.exe", path });
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", url });
sut.Perform();
- var resource = new Uri(path);
+ var resource = new Uri(url);
repository.Verify(r => r.LoadSettings(It.Is(u => u.Equals(resource)), null, null), Times.Once);
}
@@ -100,7 +88,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
repository.SetupGet(r => r.CurrentSettings).Returns(settings);
repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
sut.Perform();
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
@@ -118,7 +106,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
repository.SetupGet(r => r.CurrentSettings).Returns(settings);
repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
sut.Perform();
var resource = new Uri(Path.Combine(location, "SettingsDummy.txt"));
@@ -129,7 +117,7 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod]
public void MustFallbackToDefaultsAsLastPrio()
{
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
sut.Perform();
repository.Verify(r => r.LoadDefaultSettings(), Times.Once);
@@ -139,11 +127,11 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
public void MustAbortIfWishedByUser()
{
info.ProgramDataFolder = Path.GetDirectoryName(GetType().Assembly.Location);
- messageBox.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.Yes);
+ messageBox.Setup(m => m.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.Yes);
repository.SetupGet(r => r.CurrentSettings).Returns(settings);
repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
var result = sut.Perform();
@@ -153,15 +141,121 @@ namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
[TestMethod]
public void MustNotAbortIfNotWishedByUser()
{
- messageBox.Setup(u => u.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.No);
+ messageBox.Setup(m => m.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny())).Returns(MessageBoxResult.No);
repository.SetupGet(r => r.CurrentSettings).Returns(settings);
repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
- sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, info, text.Object, null);
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
var result = sut.Perform();
Assert.AreEqual(OperationResult.Success, result);
}
+
+ [TestMethod]
+ public void MustNotAllowToAbortIfNotInConfigureClientMode()
+ {
+ settings.ConfigurationMode = ConfigurationMode.Exam;
+ repository.SetupGet(r => r.CurrentSettings).Returns(settings);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.Success);
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
+ sut.Perform();
+
+ messageBox.Verify(m => m.Show(It.IsAny(), It.IsAny(), It.IsAny(), It.IsAny()), Times.Never);
+ }
+
+ [TestMethod]
+ public void MustNotFailWithoutCommandLineArgs()
+ {
+ repository.Setup(r => r.LoadDefaultSettings());
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, null);
+ sut.Perform();
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new string[] { });
+ sut.Perform();
+
+ repository.Verify(r => r.LoadDefaultSettings(), Times.Exactly(2));
+ }
+
+ [TestMethod]
+ public void MustNotFailWithInvalidUri()
+ {
+ var uri = @"an/invalid\uri.'*%yolo/()你好";
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", uri });
+ sut.Perform();
+ }
+
+ [TestMethod]
+ public void MustOnlyAllowToEnterAdminPasswordFiveTimes()
+ {
+ var result = new PasswordDialogResultStub { Success = true };
+ var url = @"http://www.safeexambrowser.org/whatever.seb";
+
+ passwordDialog.Setup(d => d.Show(null)).Returns(result);
+ repository.SetupGet(r => r.CurrentSettings).Returns(settings);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.AdminPasswordNeeded);
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", url });
+ sut.Perform();
+
+ repository.Verify(r => r.LoadSettings(It.IsAny(), null, null), Times.Exactly(5));
+ }
+
+ [TestMethod]
+ public void MustOnlyAllowToEnterSettingsPasswordFiveTimes()
+ {
+ var result = new PasswordDialogResultStub { Success = true };
+ var url = @"http://www.safeexambrowser.org/whatever.seb";
+
+ passwordDialog.Setup(d => d.Show(null)).Returns(result);
+ repository.SetupGet(r => r.CurrentSettings).Returns(settings);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", url });
+ sut.Perform();
+
+ repository.Verify(r => r.LoadSettings(It.IsAny(), null, null), Times.Exactly(5));
+ }
+
+ [TestMethod]
+ public void MustSucceedIfAdminPasswordCorrect()
+ {
+ var password = "test";
+ var result = new PasswordDialogResultStub { Password = password, Success = true };
+ var url = @"http://www.safeexambrowser.org/whatever.seb";
+
+ passwordDialog.Setup(d => d.Show(null)).Returns(result);
+ repository.SetupGet(r => r.CurrentSettings).Returns(settings);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.AdminPasswordNeeded);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), password, null)).Returns(LoadStatus.Success);
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", url });
+ sut.Perform();
+
+ repository.Verify(r => r.LoadSettings(It.IsAny(), null, null), Times.Exactly(1));
+ repository.Verify(r => r.LoadSettings(It.IsAny(), password, null), Times.Exactly(1));
+ }
+
+ [TestMethod]
+ public void MustSucceedIfSettingsPasswordCorrect()
+ {
+ var password = "test";
+ var result = new PasswordDialogResultStub { Password = password, Success = true };
+ var url = @"http://www.safeexambrowser.org/whatever.seb";
+
+ passwordDialog.Setup(d => d.Show(null)).Returns(result);
+ repository.SetupGet(r => r.CurrentSettings).Returns(settings);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, null)).Returns(LoadStatus.SettingsPasswordNeeded);
+ repository.Setup(r => r.LoadSettings(It.IsAny(), null, password)).Returns(LoadStatus.Success);
+
+ sut = new ConfigurationOperation(repository.Object, logger.Object, messageBox.Object, runtimeHost.Object, info, text.Object, uiFactory.Object, new[] { "blubb.exe", url });
+ sut.Perform();
+
+ repository.Verify(r => r.LoadSettings(It.IsAny(), null, null), Times.Exactly(1));
+ repository.Verify(r => r.LoadSettings(It.IsAny(), null, password), Times.Exactly(1));
+ }
}
}
diff --git a/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/PasswordDialogResultStub.cs b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/PasswordDialogResultStub.cs
new file mode 100644
index 00000000..ed553b45
--- /dev/null
+++ b/SafeExamBrowser.Runtime.UnitTests/Behaviour/Operations/PasswordDialogResultStub.cs
@@ -0,0 +1,18 @@
+/*
+ * 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.UserInterface.Windows;
+
+namespace SafeExamBrowser.Runtime.UnitTests.Behaviour.Operations
+{
+ internal class PasswordDialogResultStub : IPasswordDialogResult
+ {
+ public string Password { get; set; }
+ public bool Success { get; set; }
+ }
+}
diff --git a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
index 28350f1a..9c24e99f 100644
--- a/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
+++ b/SafeExamBrowser.Runtime.UnitTests/SafeExamBrowser.Runtime.UnitTests.csproj
@@ -81,6 +81,7 @@
+
diff --git a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
index f459e75a..a5caf563 100644
--- a/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
+++ b/SafeExamBrowser.Runtime/Behaviour/Operations/ConfigurationOperation.cs
@@ -8,7 +8,11 @@
using System;
using System.IO;
+using System.Threading;
using SafeExamBrowser.Contracts.Behaviour.OperationModel;
+using SafeExamBrowser.Contracts.Communication.Data;
+using SafeExamBrowser.Contracts.Communication.Events;
+using SafeExamBrowser.Contracts.Communication.Hosts;
using SafeExamBrowser.Contracts.Configuration;
using SafeExamBrowser.Contracts.Configuration.Settings;
using SafeExamBrowser.Contracts.I18n;
@@ -23,8 +27,10 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
private IConfigurationRepository repository;
private ILogger logger;
private IMessageBox messageBox;
- private IText text;
+ private IRuntimeHost runtimeHost;
private RuntimeInfo runtimeInfo;
+ private IText text;
+ private IUserInterfaceFactory uiFactory;
private string[] commandLineArgs;
public IProgressIndicator ProgressIndicator { private get; set; }
@@ -33,16 +39,20 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
IConfigurationRepository repository,
ILogger logger,
IMessageBox messageBox,
+ IRuntimeHost runtimeHost,
RuntimeInfo runtimeInfo,
IText text,
+ IUserInterfaceFactory uiFactory,
string[] commandLineArgs)
{
this.repository = repository;
this.logger = logger;
this.messageBox = messageBox;
this.commandLineArgs = commandLineArgs;
+ this.runtimeHost = runtimeHost;
this.runtimeInfo = runtimeInfo;
this.text = text;
+ this.uiFactory = uiFactory;
}
public OperationResult Perform()
@@ -54,22 +64,11 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
if (isValidUri)
{
- logger.Info($"Loading settings from '{uri.AbsolutePath}'...");
+ logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'...");
var result = LoadSettings(uri);
- if (result == OperationResult.Success && repository.CurrentSettings.ConfigurationMode == ConfigurationMode.ConfigureClient)
- {
- var abort = IsConfigurationSufficient();
-
- logger.Info($"The user chose to {(abort ? "abort" : "continue")} after successful client configuration.");
-
- if (abort)
- {
- return OperationResult.Aborted;
- }
- }
-
+ HandleClientConfiguration(ref result);
LogOperationResult(result);
return result;
@@ -90,7 +89,7 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
if (isValidUri)
{
- logger.Info($"Loading settings from '{uri.AbsolutePath}'...");
+ logger.Info($"Attempting to load settings from '{uri.AbsolutePath}'...");
var result = LoadSettings(uri);
@@ -117,19 +116,19 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
for (int adminAttempts = 0, settingsAttempts = 0; adminAttempts < 5 && settingsAttempts < 5;)
{
- status = repository.LoadSettings(uri, settingsPassword, adminPassword);
+ status = repository.LoadSettings(uri, adminPassword, settingsPassword);
if (status == LoadStatus.AdminPasswordNeeded || status == LoadStatus.SettingsPasswordNeeded)
{
- var isAdmin = status == LoadStatus.AdminPasswordNeeded;
- var success = isAdmin ? TryGetAdminPassword(out adminPassword) : TryGetSettingsPassword(out settingsPassword);
+ var purpose = status == LoadStatus.AdminPasswordNeeded ? PasswordRequestPurpose.Administrator : PasswordRequestPurpose.Settings;
+ var aborted = !TryGetPassword(purpose, out string password);
- if (success)
- {
- adminAttempts += isAdmin ? 1 : 0;
- settingsAttempts += isAdmin ? 0 : 1;
- }
- else
+ adminAttempts += purpose == PasswordRequestPurpose.Administrator ? 1 : 0;
+ adminPassword = purpose == PasswordRequestPurpose.Administrator ? password : adminPassword;
+ settingsAttempts += purpose == PasswordRequestPurpose.Settings ? 1 : 0;
+ settingsPassword = purpose == PasswordRequestPurpose.Settings ? password : settingsPassword;
+
+ if (aborted)
{
return OperationResult.Aborted;
}
@@ -142,45 +141,101 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
if (status == LoadStatus.InvalidData)
{
- if (IsHtmlPage(uri))
- {
- repository.LoadDefaultSettings();
- repository.CurrentSettings.Browser.StartUrl = uri.AbsoluteUri;
- logger.Info($"The specified URI '{uri.AbsoluteUri}' appears to point to a HTML page, setting it as startup URL.");
-
- return OperationResult.Success;
- }
-
- logger.Error($"The specified settings resource '{uri.AbsoluteUri}' is invalid!");
+ HandleInvalidData(ref status, uri);
}
return status == LoadStatus.Success ? OperationResult.Success : OperationResult.Failed;
}
+ private bool TryGetPassword(PasswordRequestPurpose purpose, out string password)
+ {
+ var isStartup = repository.CurrentSession == null;
+ var isRunningOnDefaultDesktop = repository.CurrentSettings?.KioskMode == KioskMode.DisableExplorerShell;
+
+ if (isStartup || isRunningOnDefaultDesktop)
+ {
+ return TryGetPasswordViaDialog(purpose, out password);
+ }
+ else
+ {
+ return TryGetPasswordViaClient(purpose, out password);
+ }
+ }
+
+ private bool TryGetPasswordViaDialog(PasswordRequestPurpose purpose, out string password)
+ {
+ var isAdmin = purpose == PasswordRequestPurpose.Administrator;
+ var message = isAdmin ? TextKey.PasswordDialog_AdminPasswordRequired : TextKey.PasswordDialog_SettingsPasswordRequired;
+ var title = isAdmin ? TextKey.PasswordDialog_AdminPasswordRequiredTitle : TextKey.PasswordDialog_SettingsPasswordRequiredTitle;
+ var dialog = uiFactory.CreatePasswordDialog(text.Get(message), text.Get(title));
+ var result = dialog.Show();
+
+ if (result.Success)
+ {
+ password = result.Password;
+ }
+ else
+ {
+ password = default(string);
+ }
+
+ return result.Success;
+ }
+
+ private bool TryGetPasswordViaClient(PasswordRequestPurpose purpose, out string password)
+ {
+ var requestId = Guid.NewGuid();
+ var response = default(PasswordEventArgs);
+ var responseEvent = new AutoResetEvent(false);
+ var responseEventHandler = new CommunicationEventHandler((args) =>
+ {
+ if (args.RequestId == requestId)
+ {
+ response = args;
+ responseEvent.Set();
+ }
+ });
+
+ runtimeHost.PasswordReceived += responseEventHandler;
+ repository.CurrentSession.ClientProxy.RequestPassword(purpose, requestId);
+ responseEvent.WaitOne();
+ runtimeHost.PasswordReceived -= responseEventHandler;
+
+ if (response.Success)
+ {
+ password = response.Password;
+ }
+ else
+ {
+ password = default(string);
+ }
+
+ return response.Success;
+ }
+
+ private void HandleInvalidData(ref LoadStatus status, Uri uri)
+ {
+ if (IsHtmlPage(uri))
+ {
+ repository.LoadDefaultSettings();
+ repository.CurrentSettings.Browser.StartUrl = uri.AbsoluteUri;
+ logger.Info($"The specified URI '{uri.AbsoluteUri}' appears to point to a HTML page, setting it as startup URL.");
+
+ status = LoadStatus.Success;
+ }
+ else
+ {
+ logger.Error($"The specified settings resource '{uri.AbsoluteUri}' is invalid!");
+ }
+ }
+
private bool IsHtmlPage(Uri uri)
{
// TODO
+
return false;
}
- private bool TryGetAdminPassword(out string password)
- {
- password = default(string);
-
- // TODO
-
- return true;
- }
-
- private bool TryGetSettingsPassword(out string password)
- {
- password = default(string);
-
- // TODO
-
- return true;
- }
-
private bool TryInitializeSettingsUri(out Uri uri)
{
var path = string.Empty;
@@ -224,6 +279,21 @@ namespace SafeExamBrowser.Runtime.Behaviour.Operations
return isValidUri;
}
+ private void HandleClientConfiguration(ref OperationResult result)
+ {
+ if (result == OperationResult.Success && repository.CurrentSettings.ConfigurationMode == ConfigurationMode.ConfigureClient)
+ {
+ var abort = IsConfigurationSufficient();
+
+ logger.Info($"The user chose to {(abort ? "abort" : "continue")} after successful client configuration.");
+
+ if (abort)
+ {
+ result = OperationResult.Aborted;
+ }
+ }
+ }
+
private bool IsConfigurationSufficient()
{
var message = text.Get(TextKey.MessageBox_ClientConfigurationQuestion);
diff --git a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
index 63cf64c3..af14c254 100644
--- a/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
+++ b/SafeExamBrowser.Runtime/Communication/RuntimeHost.cs
@@ -25,6 +25,7 @@ namespace SafeExamBrowser.Runtime.Communication
public event CommunicationEventHandler ClientDisconnected;
public event CommunicationEventHandler ClientReady;
+ public event CommunicationEventHandler PasswordReceived;
public event CommunicationEventHandler ReconfigurationRequested;
public event CommunicationEventHandler ShutdownRequested;
@@ -62,6 +63,9 @@ namespace SafeExamBrowser.Runtime.Communication
{
switch (message)
{
+ case PasswordReplyMessage r:
+ PasswordReceived?.InvokeAsync(new PasswordEventArgs { Password = r.Password, RequestId = r.RequestId, Success = r.Success });
+ return new SimpleResponse(SimpleResponsePurport.Acknowledged);
case ReconfigurationMessage r:
ReconfigurationRequested?.InvokeAsync(new ReconfigurationEventArgs { ConfigurationPath = r.ConfigurationPath });
return new SimpleResponse(SimpleResponsePurport.Acknowledged);
diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs
index fa7690c4..e4e4b9d6 100644
--- a/SafeExamBrowser.Runtime/CompositionRoot.cs
+++ b/SafeExamBrowser.Runtime/CompositionRoot.cs
@@ -63,7 +63,7 @@ namespace SafeExamBrowser.Runtime
bootstrapOperations.Enqueue(new I18nOperation(logger, text));
bootstrapOperations.Enqueue(new CommunicationOperation(runtimeHost, logger));
- sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, messageBox, runtimeInfo, text, args));
+ sessionOperations.Enqueue(new ConfigurationOperation(configuration, logger, messageBox, runtimeHost, runtimeInfo, text, uiFactory, args));
sessionOperations.Enqueue(new SessionInitializationOperation(configuration, logger, runtimeHost));
sessionOperations.Enqueue(new ServiceOperation(configuration, logger, serviceProxy, text));
sessionOperations.Enqueue(new ClientTerminationOperation(configuration, logger, processFactory, proxyFactory, runtimeHost, TEN_SECONDS));
diff --git a/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs
index 5b057bbe..f450929c 100644
--- a/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Classic/UserInterfaceFactory.cs
@@ -44,6 +44,11 @@ namespace SafeExamBrowser.UserInterface.Classic
return new BrowserWindow(control, settings);
}
+ public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
+ {
+ return new KeyboardLayoutControl();
+ }
+
public IWindow CreateLogWindow(ILogger logger)
{
LogWindow logWindow = null;
@@ -74,9 +79,9 @@ namespace SafeExamBrowser.UserInterface.Classic
return new NotificationButton(info);
}
- public ISystemKeyboardLayoutControl CreateKeyboardLayoutControl()
+ public IPasswordDialog CreatePasswordDialog(string message, string title)
{
- return new KeyboardLayoutControl();
+ throw new System.NotImplementedException();
}
public ISystemPowerSupplyControl CreatePowerSupplyControl()
diff --git a/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs b/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs
index 7db00aeb..5e0d5828 100644
--- a/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs
+++ b/SafeExamBrowser.UserInterface.Windows10/UserInterfaceFactory.cs
@@ -81,6 +81,12 @@ namespace SafeExamBrowser.UserInterface.Windows10
throw new System.NotImplementedException();
}
+ public IPasswordDialog CreatePasswordDialog(string message, string title)
+ {
+ // TODO
+ throw new System.NotImplementedException();
+ }
+
public ISystemPowerSupplyControl CreatePowerSupplyControl()
{
return new PowerSupplyControl();