patbef-ServiceOutside/ServiceOutside/Controllers/ResultsController.cs

955 lines
49 KiB
C#
Raw Permalink Normal View History

2024-01-29 16:27:34 +01:00
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");
}
}
}
}