955 lines
49 KiB
C#
955 lines
49 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
||
using ServiceShared.Database;
|
||
using ServiceShared.Models.Request;
|
||
using ServiceOutside.Service;
|
||
using ServiceShared.Crypto;
|
||
using ServiceShared.Models.Response;
|
||
using ServiceShared.Models.Response.Exception;
|
||
using ServiceShared;
|
||
using System.Text.Json;
|
||
using ServiceShared.Models.Database;
|
||
|
||
namespace ServiceOutside.Controllers
|
||
{
|
||
[ApiController]
|
||
[Route("[controller]")]
|
||
public class ResultsController : BaseController
|
||
{
|
||
private readonly ServiceShared.Database.Controllers.Results dbResults;
|
||
private readonly ServiceShared.Database.Controllers.Traces dbTraces;
|
||
private readonly int MaxOpenedRequest = 10;
|
||
private readonly int MaxTryNotFoundResults = 2;
|
||
|
||
/// <summary>
|
||
/// Constructor of ResultsController, that getting instance of configuration, dbcontext and KeyPair
|
||
/// </summary>
|
||
/// <param name="configuration">Configuration from appsettings.json</param>
|
||
/// <param name="dbContext">DbContext</param>
|
||
/// <param name="keyPair">Server Curve25519 KeyPair</param>
|
||
public ResultsController(IConfiguration configuration, DbContext dbContext, KeyPair keyPair) : base(configuration, dbContext, keyPair)
|
||
{
|
||
this.dbResults = new ServiceShared.Database.Controllers.Results(dbContext);
|
||
this.dbTraces = new ServiceShared.Database.Controllers.Traces(dbContext);
|
||
|
||
MaxOpenedRequest = configuration.GetValue<int>("MaxOpenedRequest", 10);
|
||
MaxTryNotFoundResults = configuration.GetValue<int>("MaxTryNotFoundResults", 2);
|
||
|
||
Log.Debug("MaxOpenedRequest: " + MaxOpenedRequest + ", MaxTryNotFoundResults: " + MaxTryNotFoundResults);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Subscribe request from the mobile clients
|
||
/// Subscribe must contain following data: pgs, device_token, udid, public key, device_type, verificator_hash
|
||
/// PGS: SHA512 hash from ZIP + BIRTHDAY + SAMPLEID
|
||
/// PGS_HASH: AES Encrypted PGS, that can be decrypted by inside service
|
||
/// DeviceToken: Token for push notifications
|
||
/// UDID: Unique device id
|
||
/// Public Key: Public Key of the client
|
||
/// Device Type: Type of the client (IOS or ANDROID)
|
||
/// Verificator_hash: HMAC Hash, that was created by master password of patient
|
||
///
|
||
/// RESPONSE: 1. PGS still not exists in the db and will be registred as new one
|
||
/// The Subscribe request will be redirected to the InsideService
|
||
/// to create a register ack log file
|
||
///
|
||
/// 2. PGS already exists in the db and sends AlreadySubscribtedException back
|
||
///
|
||
/// IMPORTANT: One device can have max. 10 opened request at the same time
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains Subscribe object</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or success status</returns>
|
||
[Route("subscribe")]
|
||
[HttpPost]
|
||
public JsonResult Subscribe(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
Subscribe subscribe = encrypted.Decrypt<Subscribe>(this.GetClientSharedKey());
|
||
|
||
if (subscribe != null)
|
||
{
|
||
ResponseException validationException = ServiceShared.Models.Request.Subscribe.Validate(subscribe);
|
||
|
||
if (validationException != null)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", validationException, this.GetClientSharedKey());
|
||
}
|
||
else if(string.IsNullOrEmpty(subscribe.pgs_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("pgs_hash"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
/** for first subscription pin must exist **/
|
||
if(!this.dbResults.DeviceExists(subscribe.udid) && (string.IsNullOrEmpty(subscribe.pin) || subscribe.pin.Length != 5))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("pin"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
ServiceShared.Models.Database.Results dbResults = this.dbResults.GetResults(subscribe.pgs, subscribe.udid);
|
||
|
||
// Mindmap W-1-1 -- Got a new subscribe
|
||
if (dbResults == null)
|
||
{
|
||
//check if pgs is unique
|
||
List<ServiceShared.Models.Database.Results> pgsResults = this.dbResults.GetResults(subscribe.pgs);
|
||
|
||
// PGS already subscripted
|
||
if (pgsResults != null && pgsResults.Count > 0)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new AlreadySubscribtedException(), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
// first subscription
|
||
ServiceShared.Models.Database.Results results = subscribe.ToResults();
|
||
|
||
// Mindmap W-1-5 - max opened requests(not pickedup) limit by the same device at the same time has been reached
|
||
if (this.dbResults.CountOpenedRequest(results) >= MaxOpenedRequest)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MaxOpenedRequestLimitException(), this.GetClientSharedKey());
|
||
}
|
||
// Mindmap W-1-2 - Create a new results from the subscribe request
|
||
else if (this.dbResults.Create(results))
|
||
{
|
||
// Mindmap W-1-3 - Subscribe successfully send notification to the inside service
|
||
response = new EncryptedResponse("success", null, this.GetClientSharedKey());
|
||
ServiceInside.Subscribe(subscribe);
|
||
Log.Trace("subscribted", results.PGS, results.UDID);
|
||
}
|
||
else
|
||
{
|
||
// Mindmap W-1-4 - Some unknown exception occured, results could not be created
|
||
Log.Critical(new Exception("could not create results from subscribe(request) in the database (pgs=" + results.PGS + ", udid=" + results.UDID), "ServiceOutside.Controllers.ResultsController", "Subscribe(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new UnknownException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new AlreadySubscribtedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidRequestException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Subscribe(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Unsubscribe request from the mobile clients
|
||
/// Subscribe must contain following data: pgs, device_token, udid, public key, device_type, verificator_hash
|
||
///
|
||
/// IMPORTANT: User can unsubscribe the pgs request only by authorized verificator_hash and
|
||
/// on the same device, where the request has been registred on
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains Subscribe object</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or success status</returns>
|
||
[Route("unsubscribe")]
|
||
[HttpPost]
|
||
public JsonResult Unsubscribe(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
Subscribe subscribe = encrypted.Decrypt<Subscribe>(this.GetClientSharedKey());
|
||
|
||
if (subscribe != null)
|
||
{
|
||
ResponseException validationException = ServiceShared.Models.Request.Subscribe.Validate(subscribe);
|
||
|
||
if (validationException != null)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", validationException, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
ServiceShared.Models.Database.Results dbResults = this.dbResults.GetResults(subscribe.pgs, subscribe.udid);
|
||
|
||
if (dbResults != null &&
|
||
dbResults.PGS == subscribe.pgs &&
|
||
dbResults.UDID == subscribe.udid &&
|
||
dbResults.DeviceType == subscribe.device_type &&
|
||
dbResults.VerificationHash == subscribe.verificator_hash)
|
||
{
|
||
subscribe.pgs_hash = dbResults.PGS_HASH;
|
||
|
||
if (this.dbResults.Delete(subscribe.pgs, subscribe.udid))
|
||
{
|
||
response = new EncryptedResponse("success", null, this.GetClientSharedKey());
|
||
ServiceInside.Unsubscribe(subscribe);
|
||
Log.Trace("unsubscripted", dbResults.PGS, dbResults.UDID);
|
||
}
|
||
else
|
||
{
|
||
Log.Critical(new Exception("Could not delete results from the database, PGS=" + dbResults.PGS + ", UDID=" + dbResults.UDID + ", DeviceType=" + dbResults.DeviceType.ToString() + ", VerificationHash=" + dbResults.VerificationHash), "ServiceOutside.Controllers.ResultsController", "Unsubscribe(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new UnknownException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Unsubscribe(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Updates verificator_hash for the udid
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains ChangeVerificatorHash request</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or success status</returns>
|
||
[Route("support")]
|
||
[HttpPost]
|
||
public JsonResult Support(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if(encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
ServiceShared.Models.Request.Support support = encrypted.Decrypt<ServiceShared.Models.Request.Support>(this.GetClientSharedKey());
|
||
|
||
if (support != null)
|
||
{
|
||
if (string.IsNullOrEmpty(support.udid))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("udid"), this.GetClientSharedKey());
|
||
}
|
||
else if (support.udid.Length < 10 || support.udid.Length > 64)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("udid", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
if(support.delete_device)
|
||
{
|
||
DeleteDevice deleteDevice = new DeleteDevice();
|
||
deleteDevice.udid = support.udid;
|
||
deleteDevice.verificator_hash = support.verificator_hash;
|
||
response = ServiceInside.DeleteDevice(deleteDevice);
|
||
|
||
if (response != null && response.descriptor.ToLower() == "success")
|
||
{
|
||
response = new EncryptedResponse("Success", null, this.GetClientSharedKey());
|
||
|
||
string body = "Thema: " + support.topic + "\r\n" +
|
||
"E-Mail des Absenders: " + support.email + "\r\n" +
|
||
"Nachricht: " + support.text;
|
||
|
||
this.dbTraces.DeleteDevice(support.udid);
|
||
|
||
|
||
if (!Mail.SendCustomerRequest(support.topic, body))
|
||
{
|
||
Log.Critical(new Exception("Could not send customer delete device mail"), "ServiceOutside.Controllers.ResultsController", "DeleteDevice(EncryptedRequest)");
|
||
}
|
||
}
|
||
else
|
||
{
|
||
Log.Critical(new Exception("Could not delete results for udid in the database, UDID=" + deleteDevice.udid + ", VerificationHash=" + deleteDevice.verificator_hash), "ServiceOutside.Controllers.ResultsController", "DeleteDevice(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new UnknownException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
string body = "Thema: " + support.topic + "\r\n" +
|
||
"E-Mail des Absenders: " + support.email + "\r\n" +
|
||
"Nachricht: " + support.text;
|
||
|
||
if(Mail.SendCustomerRequest(support.topic, body))
|
||
{
|
||
response = new EncryptedResponse("Success", null, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
Log.Critical(new Exception("Could not send customer mail"), "ServiceOutside.Controllers.ResultsController", "SendCustomerRequest(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new UnknownException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "DeleteDevice(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Updates verificator_hash for the udid
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains ChangeVerificatorHash request</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or success status</returns>
|
||
[Route("update_verificator_hash")]
|
||
[HttpPost]
|
||
public JsonResult UpdateVerificatorHash(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
ChangeVerificatorHash changeVerificatorHash = encrypted.Decrypt<ChangeVerificatorHash>(this.GetClientSharedKey());
|
||
|
||
if (changeVerificatorHash != null)
|
||
{
|
||
if (string.IsNullOrEmpty(changeVerificatorHash.udid))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("udid"), this.GetClientSharedKey());
|
||
}
|
||
else if (changeVerificatorHash.udid.Length < 10 || changeVerificatorHash.udid.Length > 64)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("udid", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
if (string.IsNullOrEmpty(changeVerificatorHash.old_verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("old_verificator_hash"), this.GetClientSharedKey());
|
||
}
|
||
else if (changeVerificatorHash.old_verificator_hash.Length != 128)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("old_verificator_hash", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
if (string.IsNullOrEmpty(changeVerificatorHash.new_verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("new_verificator_hash"), this.GetClientSharedKey());
|
||
}
|
||
else if (changeVerificatorHash.new_verificator_hash.Length != 128)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("new_verificator_hash", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
if (string.IsNullOrEmpty(changeVerificatorHash.pin))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("pin"), this.GetClientSharedKey());
|
||
}
|
||
else if (changeVerificatorHash.pin.Length != 5)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("pin", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
if (dbResults.UpdateVerificatorHash(changeVerificatorHash.udid, changeVerificatorHash.pin, changeVerificatorHash.old_verificator_hash, changeVerificatorHash.new_verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("Success", null, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
Log.Critical(new Exception("Could not update device verificator_hash in the database, UDID=" + changeVerificatorHash.udid + ", OldVerificationHash=" + changeVerificatorHash.old_verificator_hash + ", NewVerificationHash=" + changeVerificatorHash.new_verificator_hash), "ServiceOutside.Controllers.ResultsController", "UpdateVerificatorHash(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "UpdateVerificatorHash(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Updates DeviceToken for the udid (PUSH-Notification)
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains DeviceToken request</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or success status</returns>
|
||
[Route("update_token")]
|
||
[HttpPost]
|
||
public JsonResult DeviceToken(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
DeviceToken deviceToken = encrypted.Decrypt<DeviceToken>(this.GetClientSharedKey());
|
||
|
||
if (deviceToken != null)
|
||
{
|
||
if (string.IsNullOrEmpty(deviceToken.udid))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("udid"), this.GetClientSharedKey());
|
||
}
|
||
else if (string.IsNullOrEmpty(deviceToken.device_token))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("device_token"), this.GetClientSharedKey());
|
||
}
|
||
if (string.IsNullOrEmpty(deviceToken.verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("verificator_hash"), this.GetClientSharedKey());
|
||
}
|
||
else if (deviceToken.verificator_hash.Length != 128)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidArgumentException("verificator_hash", "wrong length"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
if (dbResults.UpdateDeviceToken(deviceToken.udid, deviceToken.verificator_hash, deviceToken.device_token))
|
||
{
|
||
response = new EncryptedResponse("Success", null, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
Log.Critical(new Exception("Could not update device token in the database, UDID=" + deviceToken.udid + ", VerificationHash=" + deviceToken.verificator_hash + ", DeviceToken=" + deviceToken.device_token), "ServiceOutside.Controllers.ResultsController", "DeviceToken(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "UpdateVerificatorHash(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns Status of results for the mobile clients
|
||
/// </summary>
|
||
// <param name="encrypted">Encrypted request, that contains Subscribe object</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or status object</returns>
|
||
[Route("status")]
|
||
[HttpPost]
|
||
public JsonResult Status(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
Subscribe subscribe = encrypted.Decrypt<Subscribe>(this.GetClientSharedKey());
|
||
|
||
if (subscribe != null)
|
||
{
|
||
ResponseException validationException = ServiceShared.Models.Request.Subscribe.Validate(subscribe);
|
||
|
||
if (validationException != null)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", validationException, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
ServiceShared.Models.Database.Results dbResults = this.dbResults.GetResults(subscribe.pgs, subscribe.udid);
|
||
|
||
if (dbResults != null)
|
||
{
|
||
if(dbResults.PGS == subscribe.pgs &&
|
||
dbResults.UDID == subscribe.udid &&
|
||
dbResults.DeviceType == subscribe.device_type &&
|
||
dbResults.VerificationHash == subscribe.verificator_hash)
|
||
{
|
||
|
||
response = new EncryptedResponse("Status", new ServiceShared.Models.Database.Status(dbResults), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAvailableException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Status(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns all of opened(still not pickedup) pgs´s requests for the mobile clients
|
||
/// </summary>
|
||
// <param name="encrypted">Encrypted request, that contains Subscribe object</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or list of status objects</returns>
|
||
[Route("opened")]
|
||
[HttpPost]
|
||
public JsonResult GetOpened(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
GetOpened openedRequest = encrypted.Decrypt<GetOpened>(this.GetClientSharedKey());
|
||
|
||
if (openedRequest != null)
|
||
{
|
||
if (string.IsNullOrEmpty(openedRequest.udid))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("udid"), this.GetClientSharedKey());
|
||
}
|
||
else if(string.IsNullOrEmpty(openedRequest.verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("verificator_hash"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
List<ServiceShared.Models.Database.Status> dbStatus = this.dbResults.GetOpened(openedRequest.udid, openedRequest.verificator_hash, MaxOpenedRequest, MaxTryNotFoundResults);
|
||
|
||
if(dbStatus == null)
|
||
{
|
||
dbStatus = new List<ServiceShared.Models.Database.Status>();
|
||
}
|
||
|
||
response = new EncryptedResponse("List<Status>", dbStatus, this.GetClientSharedKey());
|
||
|
||
if(dbStatus.Count > 0)
|
||
{
|
||
/** get not found and rejected status to remove them after response from the database **/
|
||
List<ServiceShared.Models.Database.Status> toRemoveStatuses = dbStatus.FindAll(m => !string.IsNullOrEmpty(m.results_status) && (m.results_status.ToLower() == "not_found" || m.results_status.ToLower() == "rejected"));
|
||
|
||
if(toRemoveStatuses != null)
|
||
{
|
||
this.dbResults.RemoveStatuses(toRemoveStatuses);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "GetOpened(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Download request from the mobile clients for the encrypted results
|
||
/// Subscribe must contain following data: pgs, udid, public key, device_type, verificator_hash
|
||
/// PGS: SHA512 hash from ZIP + BIRTHDAY + SAMPLEID
|
||
/// UDID: Unique device id
|
||
/// Public Key: Public Key of the client
|
||
/// Device Type: Type of the client (IOS or ANDROID)
|
||
/// Verificator_hash: HMAC Hash, that was created by master password of patient
|
||
///
|
||
/// RESPONSE: 1. Check if request is authorized
|
||
/// a: Valid PGS
|
||
/// b: It´s same Device, where the pgs was registred before (UDID & DeviceType)
|
||
/// c: Public Key from client is available for encryption
|
||
/// d: Valid verficator_hash, that was created by patient
|
||
///
|
||
/// 2. Check if results is available and is already encrypted by inside service
|
||
/// 3. Pickup encrypted file from inside service and send to the client
|
||
/// 4. Client has to decrypt first the private key of used public key by his master password
|
||
/// 5. After successfully decryption of private key user can decrypt the downloaded encrypted file by private key
|
||
/// 6. Each time if user opens the results, he has too confirm it by master password(uo 4. Steps has to be repeated)
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains Subscribe object</param>
|
||
/// <returns>Encrypted response, that contains eather encrypted content or exception</returns>
|
||
[Route("download")]
|
||
[HttpPost]
|
||
public JsonResult Download(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
Subscribe subscribe = encrypted.Decrypt<Subscribe>(this.GetClientSharedKey());
|
||
|
||
if (subscribe != null)
|
||
{
|
||
ResponseException validationException = ServiceShared.Models.Request.Subscribe.Validate(subscribe);
|
||
|
||
if (validationException != null)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", validationException, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
ServiceShared.Models.Database.Results dbResults = this.dbResults.GetResults(subscribe.pgs, subscribe.udid);
|
||
|
||
if (dbResults != null &&
|
||
dbResults.PGS == subscribe.pgs &&
|
||
dbResults.UDID == subscribe.udid &&
|
||
dbResults.DeviceType == subscribe.device_type &&
|
||
dbResults.VerificationHash == subscribe.verificator_hash)
|
||
{
|
||
if(dbResults.Available && dbResults.Status == ServiceShared.Models.Database.Results.ResultsStatus.COMPLETED)
|
||
{
|
||
Download download = ServiceInside.GetDownload(new CheckResults()
|
||
{
|
||
pgs = dbResults.PGS,
|
||
udid = dbResults.UDID
|
||
});
|
||
|
||
if(download != null)
|
||
{
|
||
response = new EncryptedResponse("Download", download, this.GetClientSharedKey());
|
||
Log.Trace("download", dbResults.PGS, dbResults.UDID);
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAvailableException(), this.GetClientSharedKey());
|
||
this.SendExceptionWithRequestAndHeaders(subscribe);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAvailableException(), this.GetClientSharedKey());
|
||
this.SendExceptionWithRequestAndHeaders(subscribe);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Download(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Updates pickedup flag and timestamp
|
||
/// CheckFileChecksum must contain following data: pgs, udid, public key, device_type, verificator_hash, file_checksum
|
||
/// PGS: SHA512 hash from ZIP + BIRTHDAY + SAMPLEID
|
||
/// UDID: Unique device id
|
||
/// Public Key: Public Key of the client
|
||
/// Device Type: Type of the client (IOS or ANDROID)
|
||
/// Verificator_hash: HMAC Hash, that was created by master password of patient
|
||
/// FileChecksum CheckSum of downloaded content after decryption
|
||
///
|
||
/// RESPONSE: Exception or Success status
|
||
/// </summary>
|
||
/// <param name="encrypted">Encrypted request, that contains CheckFileChecksum object</param>
|
||
/// <returns>Encrypted response, that contains eather encrypted content or exception</returns>
|
||
[Route("pickedup")]
|
||
[HttpPost]
|
||
public JsonResult PickedUp(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
CheckFileChecksum checkSum = encrypted.Decrypt<CheckFileChecksum>(this.GetClientSharedKey());
|
||
|
||
if (checkSum != null)
|
||
{
|
||
ResponseException validationException = ServiceShared.Models.Request.Subscribe.Validate(checkSum);
|
||
|
||
if (validationException != null)
|
||
{
|
||
response = new EncryptedResponse("ResponseException", validationException, this.GetClientSharedKey());
|
||
}
|
||
else if(string.IsNullOrEmpty(checkSum.file_checksum))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("file_checksum"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
ServiceShared.Models.Database.Results dbResults = this.dbResults.GetResults(checkSum.pgs, checkSum.udid);
|
||
|
||
if (dbResults != null &&
|
||
dbResults.PGS == checkSum.pgs &&
|
||
dbResults.UDID == checkSum.udid &&
|
||
dbResults.DeviceType == checkSum.device_type &&
|
||
dbResults.VerificationHash == checkSum.verificator_hash)
|
||
{
|
||
if(!string.IsNullOrEmpty(dbResults.FileChecksum) && dbResults.FileChecksum == checkSum.file_checksum)
|
||
{
|
||
if(this.dbResults.SetPickedUp(dbResults.PGS, dbResults.UDID))
|
||
{
|
||
response = new EncryptedResponse("success", null, this.GetClientSharedKey());
|
||
ServiceInside.PickedUp(checkSum);
|
||
Log.Trace("pickedup", dbResults.PGS, dbResults.UDID);
|
||
}
|
||
else
|
||
{
|
||
// Mindmap W-1-4 - Some unknown exception occured, results could not be created
|
||
Log.Critical(new Exception("Could not update pickedup flag to the results in the database, PGS=" + dbResults.PGS + ", UDID=" + dbResults.UDID), "ServiceOutside.Controllers.ResultsController", "PickedUp(EncryptedRequest)");
|
||
|
||
response = new EncryptedResponse("ResponseException", new UnknownException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new WrongFileChecksumException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAuthorizedException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Download(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Returns PIN of device
|
||
/// </summary>
|
||
// <param name="encrypted">Encrypted request, that contains GetPIN object</param>
|
||
/// <returns>Encrypted response, that contains eather exceptions or PIN object</returns>
|
||
[Route("pin")]
|
||
[HttpPost]
|
||
public JsonResult PIN(EncryptedRequest encrypted)
|
||
{
|
||
EncryptedResponse response = null;
|
||
|
||
try
|
||
{
|
||
this.Debug(encrypted, "REQUEST");
|
||
|
||
if (encrypted.ValidSignature(this.GetClientSharedKey(), this.GetClientSignature(), this.GetClientSignatureKey()))
|
||
{
|
||
GetPIN getPINRequest = encrypted.Decrypt<GetPIN>(this.GetClientSharedKey());
|
||
|
||
if (getPINRequest != null)
|
||
{
|
||
if (string.IsNullOrEmpty(getPINRequest.udid))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("udid"), this.GetClientSharedKey());
|
||
}
|
||
else if (string.IsNullOrEmpty(getPINRequest.verificator_hash))
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new MissingArgumentException("verificator_hash"), this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
string pin_code = this.dbResults.GetPIN(getPINRequest.udid, getPINRequest.verificator_hash);
|
||
|
||
if (!string.IsNullOrEmpty(pin_code))
|
||
{
|
||
PIN pin = new PIN();
|
||
pin.udid = getPINRequest.udid;
|
||
pin.verificator_hash = getPINRequest.verificator_hash;
|
||
pin.code = pin_code;
|
||
response = new EncryptedResponse("PIN", pin, this.GetClientSharedKey());
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new NotAvailableException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
else
|
||
{
|
||
response = new EncryptedResponse("ResponseException", new InvalidClientException(), this.GetClientSharedKey());
|
||
}
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "GetPIN(EncryptedRequest)");
|
||
}
|
||
|
||
this.Debug(response, "RESPONSE");
|
||
|
||
return new JsonResult(response);
|
||
}
|
||
|
||
/// <summary>
|
||
/// Sends Critical exception with request and headers
|
||
/// </summary>
|
||
private void SendExceptionWithRequestAndHeaders(object request)
|
||
{
|
||
try
|
||
{
|
||
string logs = "";
|
||
string body = "";
|
||
string headers = "";
|
||
|
||
if (this.HttpContext != null && this.HttpContext.Request != null && this.HttpContext.Request.Headers != null)
|
||
{
|
||
foreach (var header in this.HttpContext.Request.Headers)
|
||
{
|
||
headers += header.Key + "=" + header.Value + "\r\n";
|
||
}
|
||
}
|
||
|
||
if(request != null)
|
||
{
|
||
headers = JsonSerializer.Serialize(request, new JsonSerializerOptions
|
||
{
|
||
PropertyNameCaseInsensitive = true
|
||
});
|
||
}
|
||
|
||
logs = "\r\n\r\nHEADERS: " + headers + "\r\nREQUEST: " + body;
|
||
|
||
Log.Critical(new Exception("Trying to get not available results: " + logs), "ServiceOutside.Controllers.ResultsController", "Download(EncryptedRequest)");
|
||
}
|
||
catch (Exception ex)
|
||
{
|
||
Log.Critical(ex, "ServiceOutside.Controllers.ResultsController", "Download(not available results) -> logs");
|
||
}
|
||
}
|
||
}
|
||
}
|