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; /// /// Constructor of ResultsController, that getting instance of configuration, dbcontext and KeyPair /// /// Configuration from appsettings.json /// DbContext /// Server Curve25519 KeyPair 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("MaxOpenedRequest", 10); MaxTryNotFoundResults = configuration.GetValue("MaxTryNotFoundResults", 2); Log.Debug("MaxOpenedRequest: " + MaxOpenedRequest + ", MaxTryNotFoundResults: " + MaxTryNotFoundResults); } /// /// 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 /// /// Encrypted request, that contains Subscribe object /// Encrypted response, that contains eather exceptions or success status [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(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 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); } /// /// 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 /// /// Encrypted request, that contains Subscribe object /// Encrypted response, that contains eather exceptions or success status [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(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); } /// /// Updates verificator_hash for the udid /// /// Encrypted request, that contains ChangeVerificatorHash request /// Encrypted response, that contains eather exceptions or success status [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(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); } /// /// Updates verificator_hash for the udid /// /// Encrypted request, that contains ChangeVerificatorHash request /// Encrypted response, that contains eather exceptions or success status [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(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); } /// /// Updates DeviceToken for the udid (PUSH-Notification) /// /// Encrypted request, that contains DeviceToken request /// Encrypted response, that contains eather exceptions or success status [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(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); } /// /// Returns Status of results for the mobile clients /// // Encrypted request, that contains Subscribe object /// Encrypted response, that contains eather exceptions or status object [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(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); } /// /// Returns all of opened(still not pickedup) pgs´s requests for the mobile clients /// // Encrypted request, that contains Subscribe object /// Encrypted response, that contains eather exceptions or list of status objects [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(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 dbStatus = this.dbResults.GetOpened(openedRequest.udid, openedRequest.verificator_hash, MaxOpenedRequest, MaxTryNotFoundResults); if(dbStatus == null) { dbStatus = new List(); } response = new EncryptedResponse("List", dbStatus, this.GetClientSharedKey()); if(dbStatus.Count > 0) { /** get not found and rejected status to remove them after response from the database **/ List 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); } /// /// 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) /// /// Encrypted request, that contains Subscribe object /// Encrypted response, that contains eather encrypted content or exception [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(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); } /// /// 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 /// /// Encrypted request, that contains CheckFileChecksum object /// Encrypted response, that contains eather encrypted content or exception [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(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); } /// /// Returns PIN of device /// // Encrypted request, that contains GetPIN object /// Encrypted response, that contains eather exceptions or PIN object [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(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); } /// /// Sends Critical exception with request and headers /// 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"); } } } }