Example #1
0
        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));
        }
Example #3
0
        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));
        }
Example #4
0
        /* 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));
        }
Example #6
0
        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));
        }