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));
            }
            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("PARTICIPANT_ACCESS_ROLES").Split(","));
            fhirresourceroles.AddRange(Environment.GetEnvironmentVariable("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("GLOBAL_ACCESS_ROLES")))
            {
                if (((string)result["resourceType"]).Equals("Bundle"))
                {
                    JArray entries = (JArray)result["entry"];
                    if (!entries.IsNullOrEmpty())
                    {
                        foreach (JToken entry in entries)
                        {
                            if (!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 (!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 async Task <ProxyProcessResult> Process(string requestBody, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid)
        {
            /*Call Resource and Profile Validation server*/
            /* Pass Validation profile names in ms-fp-profile query parameter or specify empty ms-fp-profile validation parameter for schema validation only*/
            if (string.IsNullOrEmpty(requestBody) || req.Method.Equals("GET") || req.Method.Equals("DELETE"))
            {
                return(new ProxyProcessResult(true, "", requestBody, null));
            }
            if (!req.Query.ContainsKey("ms-fp-profile"))
            {
                return(new ProxyProcessResult(true, "", requestBody, null));
            }
            string url = Environment.GetEnvironmentVariable("FHIRVALIDATION_URL");

            if (string.IsNullOrEmpty(url))
            {
                log.LogWarning("The validation URL is not configured....Validatioh will not be run");
                return(new ProxyProcessResult(true, "", requestBody, null));
            }

            using (WebClient client = new WebClient())
            {
                if (req.Query.ContainsKey("ms-fp-profile"))
                {
                    foreach (string v in req.Query["ms-fp-profile"])
                    {
                        if (!string.IsNullOrEmpty(v))
                        {
                            client.QueryString.Add("profile", v);
                        }
                    }
                }
                string result = "";
                try
                {
                    byte[] response =
                        client.UploadData(url, System.Text.Encoding.UTF8.GetBytes(requestBody));
                    result = System.Text.Encoding.UTF8.GetString(response);
                }
                catch (WebException we)
                {
                    HttpWebResponse webresponse = (System.Net.HttpWebResponse)we.Response;
                    FHIRResponse    r           = new FHIRResponse();
                    r.StatusCode = webresponse.StatusCode;
                    r.Content    = Utils.genOOErrResponse("web-exception", we.Message);
                    return(new ProxyProcessResult(false, "web-exception", requestBody, r));
                }
                JObject obj = JObject.Parse(result);
                //The validator should return an OperationOutcome resource.  The presence of issues indicates a errors/warnings so we will send it back to the client for
                //corrective action
                JArray issues = (JArray)obj["issue"];
                if (!issues.IsNullOrEmpty())
                {
                    FHIRResponse resp = new FHIRResponse();
                    resp.Content    = result;
                    resp.StatusCode = HttpStatusCode.BadRequest;
                    return(new ProxyProcessResult(false, "Validation Error", requestBody, resp));
                }
            }
            return(new ProxyProcessResult(true, "", requestBody, null));
        }
Exemplo n.º 3
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 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));
        }
        public async Task <ProxyProcessResult> Process(string requestBody, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid)
        {
            /*Call Resource and Profile Validation server*/
            if (string.IsNullOrEmpty(requestBody) || req.Method.Equals("GET") || req.Method.Equals("DELETE") || string.IsNullOrEmpty(res))
            {
                return(new ProxyProcessResult(true, "", requestBody, null));
            }
            string url = Environment.GetEnvironmentVariable("FP-MOD-FHIRVALIDATION-URL");

            if (string.IsNullOrEmpty(url))
            {
                log.LogWarning("ProfileValidationPreProcess: The validation URL is not configured....Validation will not be run");
                return(new ProxyProcessResult(true, "", requestBody, null));
            }
            /* Load Profile Enforcement Policy */
            if (!init)
            {
                lock (lockObj)
                {
                    if (!init)
                    {
                        try
                        {
                            _client.BaseAddress = new Uri(url);
                            _client.Timeout     = new TimeSpan(0, 0, Utils.GetIntEnvironmentVariable("FS_TIMEOUT_SECS", "30"));
                            CloudStorageAccount storageAccount = CloudStorageAccount.Parse(Utils.GetEnvironmentVariable("FP-STORAGEACCT"));
                            // Connect to the blob storage
                            CloudBlobClient serviceClient = storageAccount.CreateCloudBlobClient();
                            // Connect to the blob container
                            CloudBlobContainer container = serviceClient.GetContainerReference(Utils.GetEnvironmentVariable("FP-MOD-FHIRVALIDATION-POLICY-CONTAINER", "fhirvalidator"));
                            // Connect to the blob file
                            CloudBlockBlob blob = container.GetBlockBlobReference(Utils.GetEnvironmentVariable("FP-MOD-FHIRVALIDATION-POLICY-FILE", "profile_enforce_policy.json"));
                            // Get the blob file as text
                            string contents = blob.DownloadTextAsync().Result;
                            enforcement = JObject.Parse(contents);
                        }
                        catch (Exception e)
                        {
                            log.LogWarning($"ProfileValidationPreProcess: Unable to load profile enforcement policy file: {e.Message} Validation against R4 structure only");
                        }
                        finally
                        {
                            init = true;
                        }
                    }
                }
            }
            string queryString = "";

            if (enforcement != null)
            {
                var enforce = enforcement["enforce"];
                var tok     = enforce.SelectToken($"[?(@.resource=='{res}')]");
                if (!tok.IsNullOrEmpty())
                {
                    JArray arr = (JArray)tok["profiles"];
                    if (!arr.IsNullOrEmpty())
                    {
                        foreach (JToken t in arr)
                        {
                            if (queryString.Length == 0)
                            {
                                queryString += $"?profile={t}";
                            }
                            else
                            {
                                queryString += $"&profile={t}";
                            }
                        }
                    }
                }
            }
            string result = "";

            try
            {
                var request = new HttpRequestMessage(HttpMethod.Post, queryString);
                request.Content = new StringContent(requestBody, System.Text.Encoding.UTF8, "application/json");
                var response = await _client.SendAsync(request);

                result = await response.Content.ReadAsStringAsync();
            }
            catch (HttpRequestException we)
            {
                FHIRResponse r = new FHIRResponse();
                r.StatusCode = HttpStatusCode.InternalServerError;
                r.Content    = Utils.genOOErrResponse("web-exception", we.Message);
                return(new ProxyProcessResult(false, "web-exception", requestBody, r));
            }

            JObject obj = JObject.Parse(result);
            //The validator should return an OperationOutcome resource.  In The presence of issues indicates a errors/warnings so we will send it back to the client for
            //corrective action
            JArray issues = (JArray)obj["issue"];

            if (!issues.IsNullOrEmpty())
            {
                FHIRResponse resp = new FHIRResponse();
                resp.Content    = result;
                resp.StatusCode = HttpStatusCode.BadRequest;
                return(new ProxyProcessResult(false, "Validation Error", requestBody, resp));
            }

            return(new ProxyProcessResult(true, "", requestBody, null));
        }
Exemplo n.º 5
0
        public async Task <ProxyProcessResult> Process(FHIRResponse response, HttpRequest req, ILogger log, ClaimsPrincipal principal, string res, string id, string hist, string vid)
        {
            try
            {
                //Do we need to send on to CDS? Don't send for GET/PATCH, errors, X-MS-FHIRCDSSynAgent Header is present
                if (req.Method.Equals("GET") || (int)response.StatusCode > 299 ||
                    req.Method.Equals("PATCH") || req.Headers["X-MS-FHIRCDSSynAgent"] == "true")
                {
                    return(new ProxyProcessResult(true, "", "", response));
                }
                InitQueueClient(log);
                if (_queueClient == null)
                {
                    log.LogWarning($"FHIRCDSSyncAgentPostProcess2: Service Bus Queue Client not initialized will not publish....Check Environment Configuration");
                    return(new ProxyProcessResult(true, "", "", response));
                }
                if (_fhirSupportedResources == null)
                {
                    log.LogWarning($"FHIRCDSSyncAgentPostProcess2: No mapped resources configured (SA-FHIRMAPPEDRESOURCES) will not publish....Check Environment Configuration");
                    return(new ProxyProcessResult(true, "", "", response));
                }
                JArray entries = null;
                if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.Created)
                {
                    var fhirresp = JObject.Parse(response.Content.ToString());
                    if (!fhirresp.IsNullOrEmpty() && ((string)fhirresp["resourceType"]).Equals("Bundle") && ((string)fhirresp["type"]).EndsWith("-response"))
                    {
                        entries = (JArray)fhirresp["entry"];
                    }
                    else
                    {
                        entries = new JArray();
                        JObject stub = new JObject();
                        stub["response"]           = new JObject();
                        stub["response"]["status"] = (int)response.StatusCode + " " + response.StatusCode.ToString();
                        stub["resource"]           = fhirresp;
                        entries.Add(stub);
                    }
                }
                else if (response.StatusCode == HttpStatusCode.NoContent)
                {
                    entries = new JArray();
                    JObject stub = new JObject();
                    stub["response"]                      = new JObject();
                    stub["response"]["status"]            = req.Method;
                    stub["resource"]                      = new JObject();
                    stub["resource"]["id"]                = id;
                    stub["resource"]["resourceType"]      = res;
                    stub["resource"]["meta"]              = new JObject();
                    stub["resource"]["meta"]["versionId"] = "1";
                    entries.Add(stub);
                }
                await publishFHIREvents(entries, log);
            }

            catch (Exception exception)
            {
                log.LogError(exception, $"FHIRCDSSyncAgentPostProcess2 Exception: {exception.Message}");
            }

            return(new ProxyProcessResult(true, "", "", response));
        }