2018-01-23 15:33:54 +01:00
|
|
|
|
/*
|
2022-01-21 16:33:52 +01:00
|
|
|
|
* Copyright (c) 2022 ETH Zürich, Educational Development and Technology (LET)
|
2018-01-23 15:33:54 +01:00
|
|
|
|
*
|
|
|
|
|
* 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/.
|
|
|
|
|
*/
|
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
using System;
|
|
|
|
|
using System.Security.AccessControl;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
using System.Threading;
|
2019-08-30 09:55:26 +02:00
|
|
|
|
using SafeExamBrowser.Communication.Contracts.Hosts;
|
|
|
|
|
using SafeExamBrowser.Communication.Contracts.Proxies;
|
|
|
|
|
using SafeExamBrowser.Configuration.Contracts;
|
|
|
|
|
using SafeExamBrowser.Core.Contracts.OperationModel;
|
|
|
|
|
using SafeExamBrowser.Core.Contracts.OperationModel.Events;
|
|
|
|
|
using SafeExamBrowser.I18n.Contracts;
|
|
|
|
|
using SafeExamBrowser.Logging.Contracts;
|
2019-09-06 08:32:29 +02:00
|
|
|
|
using SafeExamBrowser.Runtime.Operations.Events;
|
2019-12-10 14:22:18 +01:00
|
|
|
|
using SafeExamBrowser.Settings.Service;
|
2019-08-30 09:55:26 +02:00
|
|
|
|
using SafeExamBrowser.SystemComponents.Contracts;
|
|
|
|
|
using SafeExamBrowser.UserInterface.Contracts.MessageBox;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
|
2018-08-31 10:06:27 +02:00
|
|
|
|
namespace SafeExamBrowser.Runtime.Operations
|
2018-01-23 15:33:54 +01:00
|
|
|
|
{
|
2018-10-12 11:16:59 +02:00
|
|
|
|
internal class ServiceOperation : SessionOperation
|
2018-01-23 15:33:54 +01:00
|
|
|
|
{
|
|
|
|
|
private ILogger logger;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
private IRuntimeHost runtimeHost;
|
2018-01-24 12:34:32 +01:00
|
|
|
|
private IServiceProxy service;
|
2019-12-10 14:22:18 +01:00
|
|
|
|
private string serviceEventName;
|
|
|
|
|
private Guid? sessionId;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
private int timeout_ms;
|
2019-06-21 15:05:31 +02:00
|
|
|
|
private IUserInfo userInfo;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
|
2019-06-11 09:53:33 +02:00
|
|
|
|
public override event ActionRequiredEventHandler ActionRequired;
|
2018-10-12 11:16:59 +02:00
|
|
|
|
public override event StatusChangedEventHandler StatusChanged;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
public ServiceOperation(
|
|
|
|
|
ILogger logger,
|
|
|
|
|
IRuntimeHost runtimeHost,
|
|
|
|
|
IServiceProxy service,
|
|
|
|
|
SessionContext sessionContext,
|
2019-06-21 15:05:31 +02:00
|
|
|
|
int timeout_ms,
|
|
|
|
|
IUserInfo userInfo) : base(sessionContext)
|
2018-01-23 15:33:54 +01:00
|
|
|
|
{
|
|
|
|
|
this.logger = logger;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
this.runtimeHost = runtimeHost;
|
2018-10-12 11:16:59 +02:00
|
|
|
|
this.service = service;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
this.timeout_ms = timeout_ms;
|
2019-06-21 15:05:31 +02:00
|
|
|
|
this.userInfo = userInfo;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
2018-10-12 11:16:59 +02:00
|
|
|
|
public override OperationResult Perform()
|
2018-01-23 15:33:54 +01:00
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
logger.Info($"Initializing service...");
|
2018-10-03 15:42:50 +02:00
|
|
|
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
2018-01-23 15:33:54 +01:00
|
|
|
|
|
2020-06-23 13:53:10 +02:00
|
|
|
|
var success = IgnoreService() || TryInitializeConnection();
|
2018-01-24 12:34:32 +01:00
|
|
|
|
|
2019-12-10 14:22:18 +01:00
|
|
|
|
if (success && service.IsConnected)
|
2018-01-24 12:34:32 +01:00
|
|
|
|
{
|
2019-06-06 15:44:03 +02:00
|
|
|
|
success = TryStartSession();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success ? OperationResult.Success : OperationResult.Failed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public override OperationResult Repeat()
|
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
logger.Info($"Initializing service...");
|
2019-06-06 15:44:03 +02:00
|
|
|
|
StatusChanged?.Invoke(TextKey.OperationStatus_InitializeServiceSession);
|
|
|
|
|
|
2019-12-10 14:22:18 +01:00
|
|
|
|
var success = true;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
2020-06-23 13:53:10 +02:00
|
|
|
|
if (service.IsConnected)
|
2019-06-06 15:44:03 +02:00
|
|
|
|
{
|
2020-06-23 13:53:10 +02:00
|
|
|
|
if (sessionId.HasValue)
|
|
|
|
|
{
|
|
|
|
|
success = TryStopSession();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (success && IgnoreService())
|
|
|
|
|
{
|
|
|
|
|
success = TryTerminateConnection();
|
|
|
|
|
}
|
2019-06-06 15:44:03 +02:00
|
|
|
|
}
|
2020-06-23 13:53:10 +02:00
|
|
|
|
else
|
2019-06-06 15:44:03 +02:00
|
|
|
|
{
|
2020-06-23 13:53:10 +02:00
|
|
|
|
success = IgnoreService() || TryInitializeConnection();
|
2019-06-06 15:44:03 +02:00
|
|
|
|
}
|
2018-02-28 15:49:06 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
if (success && service.IsConnected)
|
|
|
|
|
{
|
|
|
|
|
success = TryStartSession();
|
2018-01-24 12:34:32 +01:00
|
|
|
|
}
|
2018-02-28 15:49:06 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
return success ? OperationResult.Success : OperationResult.Failed;
|
|
|
|
|
}
|
2018-02-28 15:49:06 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
public override OperationResult Revert()
|
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
logger.Info("Finalizing service...");
|
2019-06-06 15:44:03 +02:00
|
|
|
|
StatusChanged?.Invoke(TextKey.OperationStatus_FinalizeServiceSession);
|
|
|
|
|
|
|
|
|
|
var success = true;
|
|
|
|
|
|
|
|
|
|
if (service.IsConnected)
|
2018-03-21 10:23:15 +01:00
|
|
|
|
{
|
2019-12-10 14:22:18 +01:00
|
|
|
|
if (sessionId.HasValue)
|
2019-06-18 10:18:56 +02:00
|
|
|
|
{
|
2020-06-23 13:53:10 +02:00
|
|
|
|
success = TryStopSession(true);
|
2019-06-18 10:18:56 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
success &= TryTerminateConnection();
|
2018-03-21 10:23:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
return success ? OperationResult.Success : OperationResult.Failed;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
}
|
|
|
|
|
|
2020-06-23 13:53:10 +02:00
|
|
|
|
private bool IgnoreService()
|
|
|
|
|
{
|
|
|
|
|
if (Context.Next.Settings.Service.IgnoreService)
|
|
|
|
|
{
|
|
|
|
|
logger.Info("The service will be ignored for the next session.");
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2019-12-10 14:22:18 +01:00
|
|
|
|
private bool TryInitializeConnection()
|
2018-02-01 08:37:12 +01:00
|
|
|
|
{
|
2019-06-20 10:55:24 +02:00
|
|
|
|
var mandatory = Context.Next.Settings.Service.Policy == ServicePolicy.Mandatory;
|
|
|
|
|
var warn = Context.Next.Settings.Service.Policy == ServicePolicy.Warn;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
var connected = service.Connect();
|
|
|
|
|
var success = connected || !mandatory;
|
2018-10-10 09:19:03 +02:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
if (success)
|
2018-03-21 10:23:15 +01:00
|
|
|
|
{
|
2019-12-10 14:22:18 +01:00
|
|
|
|
logger.Info($"The service is {(mandatory ? "mandatory" : "optional")} and {(connected ? "connected." : "not connected.")}");
|
2019-06-11 09:53:33 +02:00
|
|
|
|
|
|
|
|
|
if (!connected && warn)
|
|
|
|
|
{
|
|
|
|
|
ActionRequired?.Invoke(new MessageEventArgs
|
|
|
|
|
{
|
|
|
|
|
Icon = MessageBoxIcon.Warning,
|
|
|
|
|
Message = TextKey.MessageBox_ServiceUnavailableWarning,
|
|
|
|
|
Title = TextKey.MessageBox_ServiceUnavailableWarningTitle
|
|
|
|
|
});
|
|
|
|
|
}
|
2019-06-06 15:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error("The service is mandatory but no connection could be established!");
|
2019-06-11 09:53:33 +02:00
|
|
|
|
ActionRequired?.Invoke(new MessageEventArgs
|
|
|
|
|
{
|
|
|
|
|
Icon = MessageBoxIcon.Error,
|
|
|
|
|
Message = TextKey.MessageBox_ServiceUnavailableError,
|
|
|
|
|
Title = TextKey.MessageBox_ServiceUnavailableErrorTitle
|
|
|
|
|
});
|
2018-03-21 10:23:15 +01:00
|
|
|
|
}
|
2018-02-28 15:49:06 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
return success;
|
2018-02-01 08:37:12 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
private bool TryTerminateConnection()
|
2018-01-23 15:33:54 +01:00
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
var disconnected = service.Disconnect();
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
if (disconnected)
|
2018-01-24 12:34:32 +01:00
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
logger.Info("Successfully disconnected from service.");
|
2018-01-24 12:34:32 +01:00
|
|
|
|
}
|
2019-06-06 15:44:03 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error("Failed to disconnect from service!");
|
|
|
|
|
}
|
2018-10-10 09:19:03 +02:00
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
return disconnected;
|
2018-01-23 15:33:54 +01:00
|
|
|
|
}
|
2018-02-01 08:37:12 +01:00
|
|
|
|
|
2019-06-06 15:44:03 +02:00
|
|
|
|
private bool TryStartSession()
|
2018-03-21 10:23:15 +01:00
|
|
|
|
{
|
2019-06-11 09:53:33 +02:00
|
|
|
|
var configuration = new ServiceConfiguration
|
|
|
|
|
{
|
|
|
|
|
AppConfig = Context.Next.AppConfig,
|
|
|
|
|
SessionId = Context.Next.SessionId,
|
2019-06-21 15:05:31 +02:00
|
|
|
|
Settings = Context.Next.Settings,
|
|
|
|
|
UserName = userInfo.GetUserName(),
|
|
|
|
|
UserSid = userInfo.GetUserSid()
|
2019-06-11 09:53:33 +02:00
|
|
|
|
};
|
2019-06-18 10:18:56 +02:00
|
|
|
|
var started = false;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
|
|
|
|
logger.Info("Starting new service session...");
|
|
|
|
|
|
2019-06-11 09:53:33 +02:00
|
|
|
|
var communication = service.StartSession(configuration);
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
|
|
|
|
if (communication.Success)
|
|
|
|
|
{
|
2019-06-18 10:18:56 +02:00
|
|
|
|
started = TryWaitForServiceEvent(Context.Next.AppConfig.ServiceEventName);
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
if (started)
|
2019-06-06 15:44:03 +02:00
|
|
|
|
{
|
2019-12-10 14:22:18 +01:00
|
|
|
|
sessionId = Context.Next.SessionId;
|
|
|
|
|
serviceEventName = Context.Next.AppConfig.ServiceEventName;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
logger.Info("Successfully started new service session.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"Failed to start new service session within {timeout_ms / 1000} seconds!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-07-04 09:12:28 +02:00
|
|
|
|
logger.Error("Failed to communicate session start command to service!");
|
2019-06-06 15:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
return started;
|
2018-03-21 10:23:15 +01:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-04 09:12:28 +02:00
|
|
|
|
private bool TryStopSession(bool isFinalSession = false)
|
2018-03-21 10:23:15 +01:00
|
|
|
|
{
|
2019-07-04 09:12:28 +02:00
|
|
|
|
var success = false;
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
|
|
|
|
logger.Info("Stopping current service session...");
|
|
|
|
|
|
2019-12-10 14:22:18 +01:00
|
|
|
|
var communication = service.StopSession(sessionId.Value);
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
|
|
|
|
if (communication.Success)
|
|
|
|
|
{
|
2019-12-10 14:22:18 +01:00
|
|
|
|
success = TryWaitForServiceEvent(serviceEventName);
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
2019-07-04 09:12:28 +02:00
|
|
|
|
if (success)
|
2019-06-06 15:44:03 +02:00
|
|
|
|
{
|
2019-12-10 14:22:18 +01:00
|
|
|
|
sessionId = default(Guid?);
|
|
|
|
|
serviceEventName = default(string);
|
2019-06-06 15:44:03 +02:00
|
|
|
|
logger.Info("Successfully stopped service session.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error($"Failed to stop service session within {timeout_ms / 1000} seconds!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2019-07-04 09:12:28 +02:00
|
|
|
|
logger.Error("Failed to communicate session stop command to service!");
|
2019-06-06 15:44:03 +02:00
|
|
|
|
}
|
|
|
|
|
|
2019-07-04 09:12:28 +02:00
|
|
|
|
if (success && isFinalSession)
|
|
|
|
|
{
|
|
|
|
|
communication = service.RunSystemConfigurationUpdate();
|
|
|
|
|
success = communication.Success;
|
|
|
|
|
|
|
|
|
|
if (communication.Success)
|
|
|
|
|
{
|
|
|
|
|
logger.Info("Instructed service to perform system configuration update.");
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
logger.Error("Failed to communicate system configuration update command to service!");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return success;
|
2019-06-18 10:18:56 +02:00
|
|
|
|
}
|
2019-06-06 15:44:03 +02:00
|
|
|
|
|
2019-06-18 10:18:56 +02:00
|
|
|
|
private bool TryWaitForServiceEvent(string eventName)
|
|
|
|
|
{
|
|
|
|
|
var serviceEvent = default(EventWaitHandle);
|
|
|
|
|
var startTime = DateTime.Now;
|
|
|
|
|
|
|
|
|
|
do
|
|
|
|
|
{
|
|
|
|
|
if (EventWaitHandle.TryOpenExisting(eventName, EventWaitHandleRights.Synchronize, out serviceEvent))
|
|
|
|
|
{
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
} while (startTime.AddMilliseconds(timeout_ms) > DateTime.Now);
|
|
|
|
|
|
|
|
|
|
if (serviceEvent != default(EventWaitHandle))
|
|
|
|
|
{
|
|
|
|
|
using (serviceEvent)
|
|
|
|
|
{
|
|
|
|
|
return serviceEvent.WaitOne(timeout_ms);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
2018-03-21 10:23:15 +01:00
|
|
|
|
}
|
2018-01-23 15:33:54 +01:00
|
|
|
|
}
|
|
|
|
|
}
|