private static async Task <bool> IsAParticipantOrPatient(JToken resource, FHIRClient fhirClient, IEnumerable <string> knownresourceIdentities, Dictionary <string, bool> porcache, IHeaderDictionary auditheaders) { string patientId = null; string encounterId = null; JToken patient = null; JToken encounter = null; string rt = (string)resource["resourceType"]; //Check for Patient resource or load patient resource from subject member if (rt.Equals("Patient")) { patient = resource; patientId = rt + "/" + (string)resource["id"]; } if (patient == null) { patientId = (string)resource?["subject"]?["reference"]; if (string.IsNullOrEmpty(patientId)) { patientId = (string)resource?["patient"]?["reference"]; } } if (rt.Equals("Encounter")) { encounter = resource; encounterId = rt + "/" + (string)resource["id"]; patientId = (string)resource?["subject"]?["reference"]; } if (encounter == null) { encounterId = (string)resource?["encounter"]?["reference"]; } //If no patient or encounter records present assume not tied to patient do not filter; if (patientId == null && encounterId == null) { return(true); } //See if patientId is in POR Cache if (!string.IsNullOrEmpty(patientId) && porcache.ContainsKey(patientId)) { return(porcache[patientId]); } if (!string.IsNullOrEmpty(encounterId) && porcache.ContainsKey(encounterId)) { return(porcache[encounterId]); } //Load the patient if needed if (patient == null) { if (!string.IsNullOrEmpty(patientId)) { var pat = await fhirClient.LoadResource(patientId, null, false, auditheaders); JObject temp = JObject.Parse((string)pat.Content); if (temp != null && ((string)temp["resourceType"]).Equals("Patient")) { patient = temp; } else { porcache[patientId] = false; return(false); } } else if (!string.IsNullOrEmpty(encounterId) && patient == null) { var enc = await fhirClient.LoadResource(encounterId, null, false, auditheaders); if (enc != null) { JObject temp = JObject.Parse((string)enc.Content); if (temp != null && ((string)temp["resourceType"]).Equals("Encounter") && (string)temp["subject"]?["reference"] != null) { patientId = (string)temp["subject"]?["reference"]; var pat = await fhirClient.LoadResource(patientId, null, false, auditheaders); JObject temp1 = JObject.Parse((string)pat.Content); if (temp1 != null && ((string)temp1["resourceType"]).Equals("Patient")) { patient = temp1; } else { porcache[patientId] = false; return(false); } } else { porcache[encounterId] = false; return(false); } } } else { //Cannot Determine/Find a Patient or Encounter reference assume it's not a patient reference return(true); } } foreach (string rid in knownresourceIdentities) { if (rid.StartsWith("Patient")) { string pid = rid; if (pid.Equals(patientId)) { porcache[patientId] = true; if (!string.IsNullOrEmpty(encounterId)) { porcache[encounterId] = true; } return(true); } } else if (rid.StartsWith("Practitioner")) { if (patient["generalPractitioner"] != null) { var gp_s = from gp in patient["generalPractitioner"] where (string)gp["reference"] == rid select(string) gp["reference"]; if (gp_s != null && gp_s.Count() > 0) { porcache[patientId] = true; if (!string.IsNullOrEmpty(encounterId)) { porcache[encounterId] = true; } return(true); } } string pid = rid.Split("/")[1]; string patid = (string)patient["id"]; var porencs = await fhirClient.LoadResource("Encounter", $"patient={patid}&participant={pid}", false, auditheaders); if (porencs != null) { JObject temp2 = JObject.Parse((string)porencs.Content); if (temp2 != null && ((string)temp2["resourceType"]).Equals("Bundle")) { JArray entries = (JArray)temp2["entry"]; if (entries != null && entries.Count > 0) { porcache[patientId] = true; if (!string.IsNullOrEmpty(encounterId)) { porcache[encounterId] = true; } return(true); } } } } } porcache[patientId] = false; if (!string.IsNullOrEmpty(encounterId)) { porcache[encounterId] = false; } return(false); }
private bool denyAccess(JToken resource, FHIRClient fhirclient, IDatabase cache, List <string> associations, string consentcat, IHeaderDictionary headers) { string patientId = null; string rt = resource.FHIRResourceType(); //Check for Patient resource or load patient resource id from subject/patient member if (rt.Equals("Patient")) { patientId = rt + "/" + (string)resource["id"]; } if (patientId == null) { patientId = (string)resource?["subject"]?["reference"]; if (string.IsNullOrEmpty(patientId) || !patientId.StartsWith("Patient")) { patientId = (string)resource?["patient"]?["reference"]; } } //If no patient id present assume not tied to patient do not filter; if (string.IsNullOrEmpty(patientId)) { return(false); } //Load Cache if needed List <string> denyactors = ((string)cache.StringGet($"{PATIENT_DENY_ACTORS_PREFIX}{patientId}")).DeSerializeList <string>(); if (denyactors == null) { //Fetch and Cache Deny access Consent Information var pid = patientId.Split("/")[1]; var consentrecs = fhirclient.LoadResource("Consent", "patient=" + pid + "&category=" + consentcat, true, headers); var result = (JObject)consentrecs.Content; if (result.FHIRResourceType().Equals("Bundle")) { JArray entries = (JArray)result["entry"]; if (!entries.IsNullOrEmpty()) { denyactors = new List <string>(); foreach (JToken tok in entries) { var r = tok["resource"]; if (!r["provision"].IsNullOrEmpty()) { //Check enforceemnt period if (isEnforced(r["provision"]["period"])) { string type = (string)r["provision"]["type"]; //Load deny provisions only if (type != null && type.Equals("deny")) { //Load actor references to deny JArray actors = (JArray)r["provision"]["actors"]; if (!actors.IsNullOrEmpty()) { foreach (JToken actor in actors) { denyactors.Add((string)actor["reference"]); } } else { //Nobody specified so everybody is denied access this trumps all opt out advise denyactors.Clear(); denyactors.Add("*"); break; } } } } } } else { denyactors = new List <string>(); } cache.StringSet($"{PATIENT_DENY_ACTORS_PREFIX}{patientId}", denyactors.Distinct().ToList().SerializeList(), TimeSpan.FromHours(Utils.GetIntEnvironmentVariable("CONSENT_CACHE_TTL_MINUTES", "60"))); } } //If there is an empty actor array that means there is no deny provision specified so return false to allow access if (denyactors.Count == 0) { return(false); } //It there is a wildcard in first entry then everyone is denied return true if (denyactors.First().Equals("*")) { return(true); } //Check for intersection of denied actors and associations of current user if found access will be denied return(associations.Select(x => x) .Intersect(denyactors) .Any()); }
/* 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 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("CONSENT_OPTOUT_CATEGORY"); if (string.IsNullOrEmpty(consent_category)) { log.LogWarning("ConsentOptOutFilter: No value for CONSENT_OPTOUT_CATEGORY in settings...Filter will not execute"); return(new ProxyProcessResult(true, "", "", fr)); } JObject result = (fr == null ? null : JObject.Parse(fr.ToString())); if (fr == null) { log.LogInformation("ConsentOptOutFilter: No FHIR Response found in context...Nothing to filter"); return(new ProxyProcessResult()); } //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 = 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 (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 (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)); }
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable <object> result) { var activity = await result as Activity; string msg = activity.Text.Trim().ToLower(); if (msg == "start over" || msg == "exit" || msg == "quit" || msg == "done" || msg == "start again" || msg == "restart" || msg == "leave" || msg == "reset" || msg == "bye" || msg == "goodbye") { context.PrivateConversationData.Clear(); await context.PostAsync($"Ending your session...Dont' forget to delete your conversation for privacy!"); context.Done(""); } else { model.Patient pat = null; // Within the code body set your variable string fhirserver = CloudConfigurationManager.GetSetting("FHIRServerAddress"); string fhirtenant = CloudConfigurationManager.GetSetting("FHIRAADTenant"); string fhirclientid = CloudConfigurationManager.GetSetting("FHIRClientId"); string fhirclientsecret = CloudConfigurationManager.GetSetting("FHIRClientSecret"); FHIRClient fhirclient = new FHIRClient(fhirserver, fhirtenant, fhirclientid, fhirclientsecret); var rslt = (model.Bundle)fhirclient.LoadResource("Patient", "identifier=http://fhirbot.org|" + activity.Text.Trim()); if (rslt != null && rslt.Entry != null && rslt.Entry.Count > 0) { pat = (model.Patient)rslt.Entry.FirstOrDefault().Resource; if (pat != null) { var fbid = pat.Identifier.Single(ident => ident.System == "http://fhirbot.org"); if (fbid != null) { var period = fbid.Period; var now = model.FhirDateTime.Now(); if (period != null && (now > period.EndElement)) { //Use Period Expired remove the token from the patient and update db pat.Identifier.Remove(fbid); fhirclient.SaveResource(pat); pat = null; context.PrivateConversationData.Clear(); } } } } if (pat != null) { // Set BotUserData context.PrivateConversationData.SetValue <string>( "id", pat.Id); context.PrivateConversationData.SetValue <string>( "name", pat.Name[0].Text); context.PrivateConversationData.SetValue <DateTime?>( "sessionstart", DateTime.Now); context.PrivateConversationData.SetValue <string>("bearertoken", fhirclient.BearerToken); context.PrivateConversationData.SetValue <string>("fhirserver", fhirserver); context.Done(pat.Name[0].Text); } else { context.Fail(new InvalidPINException("Not a valid PIN Code")); } } }