public async Task <ProxyProcessResult> Process(FHIRResponse response, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { if (!req.Method.Equals("GET") || response.StatusCode != HttpStatusCode.OK || response.Content == null) { return(new ProxyProcessResult(true, "", "", response)); } List <JToken> ss = null; if (DATE_SORT_RESOURCES_SUPPORTED.Contains(res) && req.Query.ContainsKey("_sort") && req.Query["_sort"].First().Contains("date")) { var fhirresp = JObject.Parse(response.Content.ToString()); if (fhirresp.IsNullOrEmpty() || !((string)fhirresp["resourceType"]).Equals("Bundle") || !((string)fhirresp["type"]).Equals("searchset")) { return(new ProxyProcessResult(true, "", "", response)); } ss = new List <JToken>(); addEntries((JArray)fhirresp["entry"], ss, log); //Process Next Pages until out or max array bool nextlink = !fhirresp["link"].IsNullOrEmpty() && ((string)fhirresp["link"].getFirstField()["relation"]).Equals("next"); while (nextlink && ss.Count < MAX_ARRAY_SIZE) { string nextpage = (string)fhirresp["link"].getFirstField()["url"]; FHIRClient fhirClient = FHIRClientFactory.getClient(log); var nextresult = await fhirClient.LoadResource(nextpage); fhirresp = JObject.Parse(nextresult.Content.ToString()); if (fhirresp.IsNullOrEmpty() || !fhirresp.FHIRResourceType().Equals("Bundle") || !((string)fhirresp["type"]).Equals("searchset")) { return(new ProxyProcessResult(false, "Next Page not Returned or server error", "", nextresult)); } addEntries((JArray)fhirresp["entry"], ss, log); nextlink = !fhirresp["link"].IsNullOrEmpty() && ((string)fhirresp["link"].getFirstField()["relation"]).Equals("next"); } ss.Sort(new DateSortedIndexComparar(req.Query["_sort"].First().Contains("-date"))); fhirresp["entry"] = new JArray(ss.ToArray()); fhirresp["link"] = new JArray(); response.Content = fhirresp.ToString(); } var retVal = new ProxyProcessResult(); if (ss != null && ss.Count >= MAX_ARRAY_SIZE) { retVal.ErrorMsg = "Warning: _Sort exceeed or met MAX_ARRAY_SIZE results may not be accurate!"; log.LogWarning("_Sort exceeed or met MAX_ARRAY_SIZE results may not be accurate!"); } retVal.Response = response; return(retVal); }
public async Task <ProxyProcessResult> Process(string requestBody, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { if (req.Method.Equals("GET") && res.SafeEquals("Patient") && !string.IsNullOrEmpty(id) && hist.SafeEquals("$everything")) { ConcurrentBag <JToken> ss = new ConcurrentBag <JToken>(); FHIRClient fhirClient = FHIRClientFactory.getClient(log); var nextresult = await fhirClient.LoadResource("Patient", "_id=" + id); var fhirresp = JObject.Parse(nextresult.Content.ToString()); if (fhirresp.IsNullOrEmpty() || !fhirresp.FHIRResourceType().Equals("Bundle") || !((string)fhirresp["type"]).Equals("searchset")) { return(new ProxyProcessResult(false, "Patient not found", "", nextresult)); } addEntries((JArray)fhirresp["entry"], ss, log, "Patient"); CountdownEvent countdown = new CountdownEvent(everyresource.Length); foreach (string rt in everyresource) { ThreadPool.QueueUserWorkItem(async delegate { try { FHIRClient fhirClient1 = FHIRClientFactory.getClient(log); string[] s = rt.Split(":"); log.LogInformation($"Loading {s[0]} resources for patient {id}"); var rslt = await fhirClient1.LoadResource(s[0], s[1].Replace("{id}", id) + "&_count=100"); var resp = JObject.Parse(rslt.Content.ToString()); if (!resp.IsNullOrEmpty() && resp.FHIRResourceType().Equals("Bundle") && ((string)resp["type"]).Equals("searchset")) { addEntries((JArray)resp["entry"], ss, log, s[0]); } countdown.Signal(); } catch (Exception e) { log.LogError($"Error fetching {rt} resources for patient {id}:{e.Message}"); countdown.Signal(); } }); } countdown.Wait(); fhirresp["entry"] = new JArray(ss.ToArray()); fhirresp["link"] = new JArray(); nextresult.Content = fhirresp; return(new ProxyProcessResult(false, "", requestBody, nextresult)); } return(new ProxyProcessResult(true, "", requestBody, null)); }
public async Task <ProxyProcessResult> Process(string requestBody, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { if (req.Method.Equals("GET") && res.SafeEquals("Patient") && !string.IsNullOrEmpty(id) && hist.SafeEquals("$everything")) { ConcurrentBag <JToken> ss = new ConcurrentBag <JToken>(); FHIRClient fhirClient = FHIRClientFactory.getClient(log); var nextresult = await fhirClient.LoadResource("Patient", "_id=" + id); var fhirresp = JObject.Parse(nextresult.Content.ToString()); if (fhirresp.IsNullOrEmpty() || fhirresp["entry"].IsNullOrEmpty()) { return(new ProxyProcessResult(false, "Patient not found or server error", "", null)); } addEntries((JArray)fhirresp["entry"], ss, log); nextresult = await fhirClient.LoadResource($"Patient/{id}/*", "_count=100"); fhirresp = JObject.Parse(nextresult.Content.ToString()); if (!fhirresp.IsNullOrEmpty() && !fhirresp["entry"].IsNullOrEmpty()) { addEntries((JArray)fhirresp["entry"], ss, log); bool nextlink = !fhirresp["link"].IsNullOrEmpty() && ((string)fhirresp["link"].getFirstField()["relation"]).Equals("next"); while (nextlink && ss.Count < MAX_ARRAY_SIZE) { string nextpage = (string)fhirresp["link"].getFirstField()["url"]; FHIRClient fhirClient1 = FHIRClientFactory.getClient(log); nextresult = await fhirClient1.LoadResource(nextpage); fhirresp = JObject.Parse(nextresult.Content.ToString()); if (fhirresp.IsNullOrEmpty() || !fhirresp.FHIRResourceType().Equals("Bundle") || !((string)fhirresp["type"]).Equals("searchset")) { return(new ProxyProcessResult(false, "Next Page not Returned or server error", "", null)); } addEntries((JArray)fhirresp["entry"], ss, log); nextlink = !fhirresp["link"].IsNullOrEmpty() && ((string)fhirresp["link"].getFirstField()["relation"]).Equals("next"); } } fhirresp["entry"] = new JArray(ss.ToArray()); fhirresp["link"] = new JArray(); nextresult.Content = fhirresp; return(new ProxyProcessResult(false, "", requestBody, nextresult)); } return(new ProxyProcessResult(true, "", requestBody, null)); }
/* Opt-out: Default is for health information of patients to be included automatically, but the patient can opt out completely. * Note: This is an access only policy, it does not prevent updates to the medical record as these could be valid */ public async Task <ProxyProcessResult> Process(FHIRResponse response, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { FHIRResponse fr = response; if (!req.Method.Equals("GET")) { return(new ProxyProcessResult(true, "", "", fr)); } //Load the consent category code from settings string consent_category = System.Environment.GetEnvironmentVariable("FP-MOD-CONSENT-OPTOUT-CATEGORY"); if (string.IsNullOrEmpty(consent_category)) { log.LogWarning("ConsentOptOutFilter: No value for FP-MOD-CONSENT-OPTOUT-CATEGORY in settings...Filter will not execute"); return(new ProxyProcessResult(true, "", "", fr)); } if (fr == null || fr.Content == null || string.IsNullOrEmpty(fr.Content.ToString())) { log.LogInformation("ConsentOptOutFilter: No FHIR Response found in context...Nothing to filter"); return(new ProxyProcessResult(true, "", "", fr)); } JObject result = JObject.Parse(fr.ToString()); //Administrator is allowed access if (Utils.inServerAccessRole(req, "A")) { return(new ProxyProcessResult(true, "", "", fr)); } ClaimsIdentity ci = (ClaimsIdentity)principal.Identity; string aadten = ci.Tenant(); string name = principal.Identity.Name; //We'll need FHIRClient to check server FHIRClient fhirClient = FHIRClientFactory.getClient(log); var cache = Utils.RedisConnection.GetDatabase(); List <string> associations = ((string)cache.StringGet($"{ASSOCIATION_CACHE_PREFIX}{aadten}-{name}")).DeSerializeList <string>(); if (associations == null) { //Load Associations if not in cache associations = new List <string>(); var table = Utils.getTable(); //Practioner var practitioner = Utils.getLinkEntity(table, "Practitioner", aadten + "-" + name); if (practitioner != null) { associations.Add(practitioner.PartitionKey + "/" + practitioner.LinkedResourceId); //Load organization from PractionerRoles var prs = await fhirClient.LoadResource("PractitionerRole", "practitioner=" + practitioner.LinkedResourceId, true, req.Headers); var ro = (JObject)prs.Content; if (ro.FHIRResourceType().Equals("Bundle")) { JArray entries = (JArray)ro["entry"]; if (!entries.IsNullOrEmpty()) { foreach (JToken tok in entries) { associations.Add(tok["resource"].FHIRReferenceId()); if (!tok["resource"]["organization"].IsNullOrEmpty() && !tok["resource"]["organization"]["reference"].IsNullOrEmpty()) { associations.Add((string)tok["resource"]["organization"]["reference"]); } } } } } //RealtedPerson var related = Utils.getLinkEntity(table, "RelatedPerson", aadten + "-" + name); if (related != null) { associations.Add(related.PartitionKey + "/" + related.LinkedResourceId); } cache.StringSet($"{ASSOCIATION_CACHE_PREFIX}{aadten}-{name}", associations.Distinct().ToList().SerializeList(), TimeSpan.FromMinutes(Utils.GetIntEnvironmentVariable("CONSENT_CACHE_TTL_MINUTES", "60"))); } //Loop through results load any consent records and validate no opt-out default is to allow if (result.FHIRResourceType().Equals("Bundle")) { JArray entries = (JArray)result["entry"]; if (!entries.IsNullOrEmpty()) { foreach (JToken tok in entries) { if (await denyAccess(tok["resource"], fhirClient, cache, associations, consent_category, req.Headers)) { JObject denyObj = new JObject(); denyObj["resourceType"] = tok["resource"].FHIRResourceType(); denyObj["id"] = tok["resource"].FHIRResourceId(); denyObj["text"] = new JObject(); denyObj["text"]["status"] = "generated"; denyObj["text"]["div"] = "<div xmlns =\"http://www.w3.org/1999/xhtml\"><p>Patient has withheld access to this resource</p></div>"; tok["resource"] = denyObj; } } } } else if (!result.FHIRResourceType().Equals("OperationalOutcome")) { if (await denyAccess(result, fhirClient, cache, associations, consent_category, req.Headers)) { fr.Content = Utils.genOOErrResponse("access-denied", $"The patient has withheld access to this resource: {res + (id == null ? "" : "/" + id)}"); fr.StatusCode = System.Net.HttpStatusCode.Unauthorized; return(new ProxyProcessResult(false, "access-denied", "", fr)); } } fr.Content = result.ToString(); return(new ProxyProcessResult(true, "", "", fr)); }
public async Task <ProxyProcessResult> Process(FHIRResponse response, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { FHIRResponse fr = response; if (!req.Method.Equals("GET")) { return(new ProxyProcessResult(true, "", "", fr)); } JObject result = (fr == null ? null : JObject.Parse(fr.ToString())); if (fr == null) { log.LogInformation("ParticipantFilter: No FHIR Response found in context"); return(new ProxyProcessResult()); } //Needed Variables ClaimsIdentity ci = (ClaimsIdentity)principal.Identity; string aadten = ci.Tenant(); string name = principal.Identity.Name; bool admin = Utils.inServerAccessRole(req, "A"); List <string> resourceidentities = new List <string>(); List <string> inroles = ci.Roles(); List <string> fhirresourceroles = new List <string>(); fhirresourceroles.AddRange(Environment.GetEnvironmentVariable("FP-PARTICIPANT-ACCESS-ROLES").Split(",")); fhirresourceroles.AddRange(Environment.GetEnvironmentVariable("FP-PATIENT-ACCESS-ROLES").Split(",")); Dictionary <string, bool> porcache = new Dictionary <string, bool>(); FHIRClient fhirClient = FHIRClientFactory.getClient(log); var table = Utils.getTable(); //Load linked Resource Identifiers from linkentities table for each known role the user is in foreach (string r in inroles) { if (fhirresourceroles.Any(r.Equals)) { var entity = Utils.getLinkEntity(table, r, aadten + "-" + name); if (entity != null) { resourceidentities.Add(r + "/" + entity.LinkedResourceId); } } } if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("FP-GLOBAL-ACCESS-ROLES"))) { if (((string)result["resourceType"]).Equals("Bundle")) { JArray entries = (JArray)result["entry"]; if (!entries.IsNullOrEmpty()) { foreach (JToken entry in entries) { if (!await IsAParticipantOrPatient(entry["resource"], fhirClient, resourceidentities, porcache, req.Headers)) { JObject denyObj = new JObject(); denyObj["resourceType"] = entry["resource"].FHIRResourceType(); denyObj["id"] = entry["resource"].FHIRResourceId(); denyObj["text"] = new JObject(); denyObj["text"]["status"] = "generated"; denyObj["text"]["div"] = "<div xmlns =\"http://www.w3.org/1999/xhtml\"><p>You do not have access to data contained in this resource</p></div>"; entry["resource"] = denyObj; } } } } else if (!((string)result["resourceType"]).Equals("OperationalOutcome")) { if (!await IsAParticipantOrPatient(result, fhirClient, resourceidentities, porcache, req.Headers)) { fr.Content = Utils.genOOErrResponse("access-denied", $"You are not an authorized Paticipant in care and cannot access this resource: {res + (id == null ? "" : "/" + id)}"); fr.StatusCode = System.Net.HttpStatusCode.Unauthorized; return(new ProxyProcessResult(false, "access-denied", "", fr)); } } } fr.Content = result.ToString(); return(new ProxyProcessResult(true, "", "", fr)); }
public ProxyProcessResult Process(string requestBody, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid) { if (string.IsNullOrEmpty(requestBody)) { return(new ProxyProcessResult(true, "", requestBody, null)); } JObject result = JObject.Parse(requestBody); if (result == null || result["resourceType"] == null) { return(new ProxyProcessResult(true, "", requestBody, null)); } if (!((string)result["resourceType"]).Equals("Bundle") || result["entry"] == null) { return(new ProxyProcessResult(true, "", requestBody, null)); } log.LogInformation($"TransformBundleProcess: looks like a valid bundle"); JArray entries = (JArray)result["entry"]; if (entries.IsNullOrEmpty()) { return(new ProxyProcessResult(true, "", requestBody, null)); } log.LogInformation($"TransformBundleProcess: Phase 1 searching for existing entries on FHIR Server..."); foreach (JToken tok in entries) { if (!tok.IsNullOrEmpty() && tok["request"]["ifNoneExist"] != null) { string resource = (string)tok["request"]["url"]; string query = (string)tok["request"]["ifNoneExist"]; log.LogInformation($"TransformBundleProcess:Loading Resource {resource} with query {query}"); var r = FHIRClientFactory.getClient(log).LoadResource(resource, query); if (r.StatusCode == System.Net.HttpStatusCode.OK) { var rs = (JObject)r.Content; if (!rs.IsNullOrEmpty() && ((string)rs["resourceType"]).Equals("Bundle") && !rs["entry"].IsNullOrEmpty()) { JArray respentries = (JArray)rs["entry"]; string existingid = "urn:uuid:" + (string)respentries[0]["resource"]["id"]; string furl = (string)tok["fullUrl"]; if (!string.IsNullOrEmpty(furl)) { requestBody.Replace(furl, existingid); } } } } } //reparse JSON with replacement of existing ids prepare to convert to Batch bundle with PUT to maintain relationships Dictionary <string, string> convert = new Dictionary <string, string>(); result = JObject.Parse(requestBody); result["type"] = "batch"; entries = (JArray)result["entry"]; foreach (JToken tok in entries) { string urn = (string)tok["fullUrl"]; if (!string.IsNullOrEmpty(urn) && urn.StartsWith("urn:uuid:") && !tok["resource"].IsNullOrEmpty()) { string rt = (string)tok["resource"]["resourceType"]; string rid = urn.Replace("urn:uuid:", ""); tok["resource"]["id"] = rid; convert.Add(rid, rt); tok["request"]["method"] = "PUT"; tok["request"]["url"] = $"{rt}?_id={rid}"; } } log.LogInformation($"TransformBundleProcess: Phase 2 Localizing {convert.Count} resource entries..."); string str = result.ToString(); foreach (string id1 in convert.Keys) { string r1 = convert[id1] + "/" + id1; string f = "urn:uuid:" + id1; str = str.Replace(f, r1); } return(new ProxyProcessResult(true, "", str, null)); }