diff --git a/SafeExamBrowser.Client/Behaviour/Operations/ConfigurationOperation.cs b/SafeExamBrowser.Client/Behaviour/Operations/ConfigurationOperation.cs index ca6dd33a..16097709 100644 --- a/SafeExamBrowser.Client/Behaviour/Operations/ConfigurationOperation.cs +++ b/SafeExamBrowser.Client/Behaviour/Operations/ConfigurationOperation.cs @@ -45,6 +45,9 @@ namespace SafeExamBrowser.Client.Behaviour.Operations configuration.Settings = config.Settings; logger.Info("Successfully retrieved the application configuration from the runtime."); + logger.Info($" -> Client-ID: {configuration.RuntimeInfo.ClientId}"); + logger.Info($" -> Runtime-ID: {configuration.RuntimeInfo.RuntimeId}"); + logger.Info($" -> Session-ID: {configuration.SessionId}"); } catch (Exception e) { diff --git a/SafeExamBrowser.Configuration/ConfigurationRepository.cs b/SafeExamBrowser.Configuration/ConfigurationRepository.cs index 496df28c..c4b830b3 100644 --- a/SafeExamBrowser.Configuration/ConfigurationRepository.cs +++ b/SafeExamBrowser.Configuration/ConfigurationRepository.cs @@ -16,6 +16,9 @@ namespace SafeExamBrowser.Configuration { public class ConfigurationRepository : IConfigurationRepository { + private const string BASE_ADDRESS = "net.pipe://localhost/safeexambrowser"; + + private bool firstSession = true; private RuntimeInfo runtimeInfo; public ISessionData CurrentSession { get; private set; } @@ -35,19 +38,6 @@ namespace SafeExamBrowser.Configuration } } - public ISessionData InitializeSessionData() - { - var session = new SessionData - { - Id = Guid.NewGuid(), - StartupToken = Guid.NewGuid() - }; - - CurrentSession = session; - - return session; - } - public ClientConfiguration BuildClientConfiguration() { return new ClientConfiguration @@ -58,6 +48,24 @@ namespace SafeExamBrowser.Configuration }; } + public void InitializeSessionConfiguration() + { + CurrentSession = new SessionData + { + Id = Guid.NewGuid(), + StartupToken = Guid.NewGuid() + }; + + if (!firstSession) + { + UpdateRuntimeInfo(); + } + else + { + firstSession = false; + } + } + public Settings LoadSettings(Uri path) { // TODO: Implement loading mechanism @@ -92,10 +100,7 @@ namespace SafeExamBrowser.Configuration private void InitializeRuntimeInfo() { var appDataFolder = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(SafeExamBrowser)); - var baseAddress = "net.pipe://localhost/safeexambrowser"; - var clientId = Guid.NewGuid(); var executable = Assembly.GetEntryAssembly(); - var runtimeId = Guid.NewGuid(); var startTime = DateTime.Now; var logFolder = Path.Combine(appDataFolder, "Logs"); var logFilePrefix = startTime.ToString("yyyy-MM-dd\\_HH\\hmm\\mss\\s"); @@ -107,7 +112,7 @@ namespace SafeExamBrowser.Configuration BrowserCachePath = Path.Combine(appDataFolder, "Cache"), BrowserLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Browser.txt"), ClientId = Guid.NewGuid(), - ClientAddress = $"{baseAddress}/client/{clientId}", + ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}", ClientExecutablePath = Path.Combine(Path.GetDirectoryName(executable.Location), $"{nameof(SafeExamBrowser)}.Client.exe"), ClientLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Client.txt"), DefaultSettingsFileName = "SebClientSettings.seb", @@ -116,10 +121,16 @@ namespace SafeExamBrowser.Configuration ProgramTitle = executable.GetCustomAttribute().Title, ProgramVersion = executable.GetCustomAttribute().InformationalVersion, RuntimeId = Guid.NewGuid(), - RuntimeAddress = $"{baseAddress}/runtime/{runtimeId}", + RuntimeAddress = $"{BASE_ADDRESS}/runtime/{Guid.NewGuid()}", RuntimeLogFile = Path.Combine(logFolder, $"{logFilePrefix}_Runtime.txt"), - ServiceAddress = $"{baseAddress}/service" + ServiceAddress = $"{BASE_ADDRESS}/service" }; } + + private void UpdateRuntimeInfo() + { + RuntimeInfo.ClientId = Guid.NewGuid(); + RuntimeInfo.ClientAddress = $"{BASE_ADDRESS}/client/{Guid.NewGuid()}"; + } } } diff --git a/SafeExamBrowser.Configuration/SessionData.cs b/SafeExamBrowser.Configuration/SessionData.cs index 89d0c1e4..f872fef7 100644 --- a/SafeExamBrowser.Configuration/SessionData.cs +++ b/SafeExamBrowser.Configuration/SessionData.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.Configuration; using SafeExamBrowser.Contracts.WindowsApi; @@ -14,6 +15,7 @@ namespace SafeExamBrowser.Configuration { public class SessionData : ISessionData { + public IClientProxy ClientProxy { get; set; } public IProcess ClientProcess { get; set; } public Guid Id { get; set; } public Guid StartupToken { get; set; } diff --git a/SafeExamBrowser.Contracts/Communication/IProxyFactory.cs b/SafeExamBrowser.Contracts/Communication/IProxyFactory.cs new file mode 100644 index 00000000..920540e3 --- /dev/null +++ b/SafeExamBrowser.Contracts/Communication/IProxyFactory.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.Communication +{ + /// + /// A factory to create communication proxies during application runtime. + /// + public interface IProxyFactory + { + /// + /// Creates a new for the given endpoint address. + /// + IClientProxy CreateClientProxy(string address); + } +} diff --git a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs index 71913a3e..4e1ca43d 100644 --- a/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs +++ b/SafeExamBrowser.Contracts/Configuration/IConfigurationRepository.cs @@ -45,7 +45,7 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// Initializes all relevant data for a new session. /// - ISessionData InitializeSessionData(); + void InitializeSessionConfiguration(); /// /// Attempts to load settings from the specified path. diff --git a/SafeExamBrowser.Contracts/Configuration/ISessionData.cs b/SafeExamBrowser.Contracts/Configuration/ISessionData.cs index 2455fdbc..3cdcda22 100644 --- a/SafeExamBrowser.Contracts/Configuration/ISessionData.cs +++ b/SafeExamBrowser.Contracts/Configuration/ISessionData.cs @@ -7,6 +7,7 @@ */ using System; +using SafeExamBrowser.Contracts.Communication; using SafeExamBrowser.Contracts.WindowsApi; namespace SafeExamBrowser.Contracts.Configuration @@ -16,6 +17,11 @@ namespace SafeExamBrowser.Contracts.Configuration /// public interface ISessionData { + /// + /// The communication proxy for the client instance associated to this session. + /// + IClientProxy ClientProxy { get; set; } + /// /// The process information of the client instance associated to this session. /// diff --git a/SafeExamBrowser.Contracts/Configuration/RuntimeInfo.cs b/SafeExamBrowser.Contracts/Configuration/RuntimeInfo.cs index f61985df..41581069 100644 --- a/SafeExamBrowser.Contracts/Configuration/RuntimeInfo.cs +++ b/SafeExamBrowser.Contracts/Configuration/RuntimeInfo.cs @@ -38,9 +38,6 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// The communication address of the client component. - /// - /// TODO: Will need to be updated for each new client instance! - /// /// public string ClientAddress { get; set; } @@ -51,9 +48,6 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// The unique identifier for the currently running client instance. - /// - /// TODO: Will need to be updated for each new client instance! -> Remove if unused! - /// /// public Guid ClientId { get; set; } @@ -94,9 +88,6 @@ namespace SafeExamBrowser.Contracts.Configuration /// /// The unique identifier for the currently running runtime instance. - /// - /// TODO: Remove if unused! - /// /// public Guid RuntimeId { get; set; } diff --git a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj index 1336c27c..e1e16adc 100644 --- a/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj +++ b/SafeExamBrowser.Contracts/SafeExamBrowser.Contracts.csproj @@ -62,6 +62,7 @@ + diff --git a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs index 25ffc87c..63ee08e5 100644 --- a/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs +++ b/SafeExamBrowser.Runtime/Behaviour/RuntimeController.cs @@ -22,7 +22,6 @@ namespace SafeExamBrowser.Runtime.Behaviour { private bool sessionRunning; - private IClientProxy client; private IConfigurationRepository configuration; private ILogger logger; private IOperationSequence bootstrapSequence; @@ -36,7 +35,6 @@ namespace SafeExamBrowser.Runtime.Behaviour private IUserInterfaceFactory uiFactory; public RuntimeController( - IClientProxy client, IConfigurationRepository configuration, ILogger logger, IOperationSequence bootstrapSequence, @@ -47,7 +45,6 @@ namespace SafeExamBrowser.Runtime.Behaviour Action shutdown, IUserInterfaceFactory uiFactory) { - this.client = client; this.configuration = configuration; this.logger = logger; this.bootstrapSequence = bootstrapSequence; @@ -202,7 +199,6 @@ namespace SafeExamBrowser.Runtime.Behaviour private void RegisterEvents() { - client.ConnectionLost += Client_ConnectionLost; runtimeHost.ReconfigurationRequested += RuntimeHost_ReconfigurationRequested; runtimeHost.ShutdownRequested += RuntimeHost_ShutdownRequested; } @@ -210,11 +206,11 @@ namespace SafeExamBrowser.Runtime.Behaviour private void RegisterSessionEvents() { configuration.CurrentSession.ClientProcess.Terminated += ClientProcess_Terminated; + configuration.CurrentSession.ClientProxy.ConnectionLost += Client_ConnectionLost; } private void DeregisterEvents() { - client.ConnectionLost -= Client_ConnectionLost; runtimeHost.ReconfigurationRequested -= RuntimeHost_ReconfigurationRequested; runtimeHost.ShutdownRequested -= RuntimeHost_ShutdownRequested; } @@ -222,6 +218,7 @@ namespace SafeExamBrowser.Runtime.Behaviour private void DeregisterSessionEvents() { configuration.CurrentSession.ClientProcess.Terminated -= ClientProcess_Terminated; + configuration.CurrentSession.ClientProxy.ConnectionLost -= Client_ConnectionLost; } private void ClientProcess_Terminated(int exitCode) diff --git a/SafeExamBrowser.Runtime/Behaviour/SessionController.cs b/SafeExamBrowser.Runtime/Behaviour/SessionController.cs index 407ad16c..3d0d1378 100644 --- a/SafeExamBrowser.Runtime/Behaviour/SessionController.cs +++ b/SafeExamBrowser.Runtime/Behaviour/SessionController.cs @@ -22,28 +22,27 @@ namespace SafeExamBrowser.Runtime.Behaviour private const int TEN_SECONDS = 10000; private bool sessionRunning; - private IClientProxy client; private IConfigurationRepository configuration; private ILogger logger; private IProcessFactory processFactory; + private IProxyFactory proxyFactory; private IRuntimeHost runtimeHost; private IServiceProxy service; - private ISessionData session; internal IProgressIndicator ProgressIndicator { private get; set; } internal SessionController( - IClientProxy client, IConfigurationRepository configuration, ILogger logger, IProcessFactory processFactory, + IProxyFactory proxyFactory, IRuntimeHost runtimeHost, IServiceProxy service) { - this.client = client; this.configuration = configuration; this.logger = logger; this.processFactory = processFactory; + this.proxyFactory = proxyFactory; this.runtimeHost = runtimeHost; this.service = service; } @@ -53,23 +52,19 @@ namespace SafeExamBrowser.Runtime.Behaviour logger.Info("Starting new session..."); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StartSession, true); - session = configuration.InitializeSessionData(); - runtimeHost.StartupToken = session.StartupToken; - - logger.Info("Initializing service session..."); - service.StartSession(session.Id, configuration.CurrentSettings); - + InitializeSessionConfiguration(); + StartServiceSession(); sessionRunning = TryStartClient(); if (!sessionRunning) { logger.Info($"Failed to start new session! Reverting service session and aborting procedure..."); - service.StopSession(session.Id); + service.StopSession(configuration.CurrentSession.Id); return OperationResult.Failed; } - logger.Info($"Successfully started new session with identifier '{session.Id}'."); + logger.Info($"Successfully started new session."); return OperationResult.Success; } @@ -78,24 +73,45 @@ namespace SafeExamBrowser.Runtime.Behaviour { if (sessionRunning) { - logger.Info($"Stopping session with identifier '{session.Id}'..."); + logger.Info($"Stopping session with identifier '{configuration.CurrentSession.Id}'..."); ProgressIndicator?.UpdateText(TextKey.ProgressIndicator_StopSession, true); - logger.Info("Stopping service session..."); - service.StopSession(session.Id); + StopServiceSession(); - if (!session.ClientProcess.HasTerminated) + if (!configuration.CurrentSession.ClientProcess.HasTerminated) { StopClient(); } sessionRunning = false; - logger.Info($"Successfully stopped session with identifier '{session.Id}'."); + logger.Info($"Successfully stopped session."); } return OperationResult.Success; } + private void InitializeSessionConfiguration() + { + configuration.InitializeSessionConfiguration(); + runtimeHost.StartupToken = configuration.CurrentSession.StartupToken; + + logger.Info($" -> Client-ID: {configuration.RuntimeInfo.ClientId}"); + logger.Info($" -> Runtime-ID: {configuration.RuntimeInfo.RuntimeId} (as reference, does not change)"); + logger.Info($" -> Session-ID: {configuration.CurrentSession.Id}"); + } + + private void StartServiceSession() + { + logger.Info("Initializing service session..."); + service.StartSession(configuration.CurrentSession.Id, configuration.CurrentSettings); + } + + private void StopServiceSession() + { + logger.Info("Stopping service session..."); + service.StopSession(configuration.CurrentSession.Id); + } + private bool TryStartClient() { var clientReady = false; @@ -104,11 +120,11 @@ namespace SafeExamBrowser.Runtime.Behaviour var clientExecutable = configuration.RuntimeInfo.ClientExecutablePath; var clientLogFile = $"{'"' + configuration.RuntimeInfo.ClientLogFile + '"'}"; var hostUri = configuration.RuntimeInfo.RuntimeAddress; - var token = session.StartupToken.ToString("D"); + var token = configuration.CurrentSession.StartupToken.ToString("D"); - logger.Info("Starting new client process."); + logger.Info("Starting new client process..."); runtimeHost.ClientReady += clientReadyEventHandler; - session.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token); + configuration.CurrentSession.ClientProcess = processFactory.StartNew(clientExecutable, clientLogFile, hostUri, token); logger.Info("Waiting for client to complete initialization..."); clientReady = clientReadyEvent.WaitOne(TEN_SECONDS); @@ -122,8 +138,10 @@ namespace SafeExamBrowser.Runtime.Behaviour } logger.Info("Client has been successfully started and initialized."); + logger.Info("Creating communication proxy for client host..."); + configuration.CurrentSession.ClientProxy = proxyFactory.CreateClientProxy(configuration.RuntimeInfo.ClientAddress); - if (!client.Connect(session.StartupToken)) + if (!configuration.CurrentSession.ClientProxy.Connect(configuration.CurrentSession.StartupToken)) { logger.Error("Failed to connect to client!"); @@ -132,16 +150,16 @@ namespace SafeExamBrowser.Runtime.Behaviour logger.Info("Connection with client has been established."); - var response = client.RequestAuthentication(); + var response = configuration.CurrentSession.ClientProxy.RequestAuthentication(); - if (session.ClientProcess.Id != response?.ProcessId) + if (configuration.CurrentSession.ClientProcess.Id != response?.ProcessId) { logger.Error("Failed to verify client integrity!"); return false; } - logger.Info("Authentication of client has been successful."); + logger.Info("Authentication of client has been successful, client is ready to operate."); return true; } @@ -157,13 +175,13 @@ namespace SafeExamBrowser.Runtime.Behaviour var terminatedEventHandler = new ProcessTerminatedEventHandler((_) => terminatedEvent.Set()); runtimeHost.ClientDisconnected += disconnectedEventHandler; - session.ClientProcess.Terminated += terminatedEventHandler; + configuration.CurrentSession.ClientProcess.Terminated += terminatedEventHandler; logger.Info("Instructing client to initiate shutdown procedure."); - client.InitiateShutdown(); + configuration.CurrentSession.ClientProxy.InitiateShutdown(); logger.Info("Disconnecting from client communication host."); - client.Disconnect(); + configuration.CurrentSession.ClientProxy.Disconnect(); logger.Info("Waiting for client to disconnect from runtime communication host..."); disconnected = disconnectedEvent.WaitOne(TEN_SECONDS); @@ -182,7 +200,7 @@ namespace SafeExamBrowser.Runtime.Behaviour } runtimeHost.ClientDisconnected -= disconnectedEventHandler; - session.ClientProcess.Terminated -= terminatedEventHandler; + configuration.CurrentSession.ClientProcess.Terminated -= terminatedEventHandler; if (disconnected && terminated) { @@ -206,10 +224,10 @@ namespace SafeExamBrowser.Runtime.Behaviour return; } - logger.Info($"Killing client process with ID = {session.ClientProcess.Id}."); - session.ClientProcess.Kill(); + logger.Info($"Killing client process with ID = {configuration.CurrentSession.ClientProcess.Id}."); + configuration.CurrentSession.ClientProcess.Kill(); - if (session.ClientProcess.HasTerminated) + if (configuration.CurrentSession.ClientProcess.HasTerminated) { logger.Info("Client process has terminated."); } diff --git a/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs b/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs new file mode 100644 index 00000000..12ae7cc8 --- /dev/null +++ b/SafeExamBrowser.Runtime/Communication/ProxyFactory.cs @@ -0,0 +1,30 @@ +/* + * 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.Communication; +using SafeExamBrowser.Contracts.Logging; +using SafeExamBrowser.Core.Communication; +using SafeExamBrowser.Core.Logging; + +namespace SafeExamBrowser.Runtime.Communication +{ + internal class ProxyFactory : IProxyFactory + { + private ILogger logger; + + public ProxyFactory(ILogger logger) + { + this.logger = logger; + } + + public IClientProxy CreateClientProxy(string address) + { + return new ClientProxy(address, new ModuleLogger(logger, typeof(ClientProxy))); + } + } +} diff --git a/SafeExamBrowser.Runtime/CompositionRoot.cs b/SafeExamBrowser.Runtime/CompositionRoot.cs index fb9912aa..5887fac9 100644 --- a/SafeExamBrowser.Runtime/CompositionRoot.cs +++ b/SafeExamBrowser.Runtime/CompositionRoot.cs @@ -49,10 +49,10 @@ namespace SafeExamBrowser.Runtime var uiFactory = new UserInterfaceFactory(text); var desktop = new Desktop(new ModuleLogger(logger, typeof(Desktop))); var processFactory = new ProcessFactory(desktop, new ModuleLogger(logger, typeof(ProcessFactory))); - var clientProxy = new ClientProxy(runtimeInfo.ClientAddress, new ModuleLogger(logger, typeof(ClientProxy))); + var proxyFactory = new ProxyFactory(logger); var runtimeHost = new RuntimeHost(runtimeInfo.RuntimeAddress, configuration, new ModuleLogger(logger, typeof(RuntimeHost))); var serviceProxy = new ServiceProxy(runtimeInfo.ServiceAddress, new ModuleLogger(logger, typeof(ServiceProxy))); - var sessionController = new SessionController(clientProxy, configuration, logger, processFactory, runtimeHost, serviceProxy); + var sessionController = new SessionController(configuration, logger, processFactory, proxyFactory, runtimeHost, serviceProxy); var bootstrapOperations = new Queue(); var sessionOperations = new Queue(); @@ -69,7 +69,7 @@ namespace SafeExamBrowser.Runtime var boostrapSequence = new OperationSequence(logger, bootstrapOperations); var sessionSequence = new OperationSequence(logger, sessionOperations); - RuntimeController = new RuntimeController(clientProxy, configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, shutdown, uiFactory); + RuntimeController = new RuntimeController(configuration, logger, boostrapSequence, sessionSequence, runtimeHost, runtimeInfo, serviceProxy, shutdown, uiFactory); } internal void LogStartupInformation() @@ -81,6 +81,7 @@ namespace SafeExamBrowser.Runtime logger.Log(string.Empty); logger.Log($"# Application started at {runtimeInfo.ApplicationStartTime.ToString("yyyy-MM-dd HH:mm:ss.fff")}"); logger.Log($"# Running on {systemInfo.OperatingSystemInfo}"); + logger.Log($"# Runtime-ID: {runtimeInfo.RuntimeId}"); logger.Log(string.Empty); } diff --git a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj index 8b75d2e9..f8decb12 100644 --- a/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj +++ b/SafeExamBrowser.Runtime/SafeExamBrowser.Runtime.csproj @@ -93,6 +93,7 @@ +