public static FHIRClient getClient(ILogger log)
 {
     /* Get/update/check current bearer token to authenticate the proxy to the FHIR Server
      * The following parameters must be defined in environment variables:
      * To use Manged Service Identity or Service Client:
      * FS_URL = the fully qualified URL to the FHIR Server
      * FS_RESOURCE = the audience or resource for the FHIR Server for Azure API for FHIR should be https://azurehealthcareapis.com
      * To use a Service Client Principal the following must also be specified:
      * FS_TENANT_NAME = the GUID or UPN of the AAD tenant that is hosting FHIR Server Authentication
      * FS_CLIENT_ID = the client or app id of the private client authorized to access the FHIR Server
      * FS_SECRET = the client secret to pass to FHIR Server Authentication
      */
     if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FS_RESOURCE")) && FHIRClient.isTokenExpired(_bearerToken))
     {
         lock (_lock)
         {
             if (FHIRClient.isTokenExpired(_bearerToken))
             {
                 log.LogInformation("Token is expired...Obtaining new bearer token...");
                 _bearerToken = FHIRClient.GetOAUTH2BearerToken(System.Environment.GetEnvironmentVariable("FS_RESOURCE"), System.Environment.GetEnvironmentVariable("FS_TENANT_NAME"),
                                                                System.Environment.GetEnvironmentVariable("FS_CLIENT_ID"), System.Environment.GetEnvironmentVariable("FS_SECRET")).GetAwaiter().GetResult();
             }
         }
     }
     /* Get a FHIR Client instance to talk to the FHIR Server */
     return(new FHIRClient(System.Environment.GetEnvironmentVariable("FS_URL"), _bearerToken));
 }
        public static async Task <FHIRResponse> callFHIRServer(string requestBody, HttpRequest req, ILogger log, string res, string id, string hist, string vid)
        {
            FHIRClient fhirClient = FHIRClientFactory.getClient(log);

            FHIRResponse fhirresp = null;

            if (req.Method.Equals("GET"))
            {
                var           qs = req.QueryString.HasValue ? req.QueryString.Value : null;
                StringBuilder sb = new StringBuilder();
                sb.Append(res);
                if (!string.IsNullOrEmpty(id))
                {
                    sb.Append("/" + id);
                    if (!string.IsNullOrEmpty(hist))
                    {
                        sb.Append("/" + hist);
                        if (!string.IsNullOrEmpty(vid))
                        {
                            sb.Append("/" + vid);
                        }
                    }
                }
                fhirresp = await fhirClient.LoadResource(sb.ToString(), qs, false, req.Headers);
            }
            else
            {
                if (req.Method.Equals("DELETE"))
                {
                    fhirresp = await fhirClient.DeleteResource(res + (id == null ? "" : "/" + id), req.Headers);
                }
                else if (req.Method.Equals("POST") && !string.IsNullOrEmpty(id) && id.StartsWith("_search"))
                {
                    var qs = req.QueryString.HasValue ? req.QueryString.Value : null;
                    fhirresp = await fhirClient.PostCommand(res + "/" + id, requestBody, qs, req.Headers);
                }
                else
                {
                    fhirresp = await fhirClient.SaveResource(res, requestBody, req.Method, req.Headers);
                }
            }
            return(fhirresp);
        }
Пример #3
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "manage/{cmd}/{res}/{id}/{name}")] HttpRequest req,
            ILogger log, ClaimsPrincipal principal, string cmd, string res, string id, string name)
        {
            log.LogInformation("SecureLink Function Invoked");
            //Is the principal authenticated
            if (!Utils.isServerAccessAuthorized(req))
            {
                return(new ContentResult()
                {
                    Content = "User is not Authenticated", StatusCode = (int)System.Net.HttpStatusCode.Unauthorized
                });
            }

            if (!Utils.inServerAccessRole(req, "A"))
            {
                return(new ContentResult()
                {
                    Content = "User does not have suffiecient rights (Administrator required)", StatusCode = (int)System.Net.HttpStatusCode.Unauthorized
                });
            }
            if (string.IsNullOrEmpty(cmd) || !validcmds.Any(cmd.Contains))
            {
                return(new BadRequestObjectResult("Invalid Command....Valid commands are link, unlink and list"));
            }
            //Are we linking the correct resource type
            if (string.IsNullOrEmpty(res) || !allowedresources.Any(res.Contains))
            {
                return(new BadRequestObjectResult("Resource must be Patient,Practitioner or RelatedPerson"));
            }

            ClaimsIdentity ci      = (ClaimsIdentity)principal.Identity;
            string         aadten  = (string.IsNullOrEmpty(ci.Tenant()) ? "Unknown" : ci.Tenant());
            FhirJsonParser _parser = new FhirJsonParser();

            _parser.Settings.AcceptUnknownMembers   = true;
            _parser.Settings.AllowUnrecognizedEnums = true;
            //Get a FHIR Client so we can talk to the FHIR Server
            log.LogInformation($"Instanciating FHIR Client Proxy");
            FHIRClient fhirClient  = FHIRClientFactory.getClient(log);
            int        i_link_days = 0;

            int.TryParse(System.Environment.GetEnvironmentVariable("FP-LINK-DAYS"), out i_link_days);
            if (i_link_days == 0)
            {
                i_link_days = 365;
            }
            //Load the resource to Link
            var fhirresp = await fhirClient.LoadResource(res + "/" + id, null, false, req.Headers);

            var lres = _parser.Parse <Resource>((string)fhirresp.Content);

            if (lres.ResourceType == Hl7.Fhir.Model.ResourceType.OperationOutcome)
            {
                return(new BadRequestObjectResult(lres.ToString()));
            }
            CloudTable table = Utils.getTable();

            switch (cmd)
            {
            case "link":
                LinkEntity linkentity = new LinkEntity(res, aadten + "-" + name);
                linkentity.ValidUntil       = DateTime.Now.AddDays(i_link_days);
                linkentity.LinkedResourceId = id;
                Utils.setLinkEntity(table, linkentity);
                return(new OkObjectResult($"Identity: {name} in directory {aadten} is now linked to {res}/{id}"));

            case "unlink":
                LinkEntity delentity = Utils.getLinkEntity(table, res, aadten + "-" + name);
                if (delentity == null)
                {
                    return(new OkObjectResult($"Resource {res}/{id} has no links to Identity {name} in directory {aadten}"));
                }
                Utils.deleteLinkEntity(table, delentity);
                return(new OkObjectResult($"Identity: {name} in directory {aadten} has been unlinked from {res}/{id}"));

            case "list":
                LinkEntity entity = Utils.getLinkEntity(table, res, aadten + "-" + name);
                if (entity != null)
                {
                    return(new OkObjectResult($"Resource {res}/{id} is linked to Identity: {name} in directory {aadten}"));
                }
                else
                {
                    return(new OkObjectResult($"Resource {res}/{id} has no links to Identity {name} in directory {aadten}"));
                }
            }
            return(new OkObjectResult($"No action taken Identity: {name}"));
        }
Пример #4
0
        public static IActionResult Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "linkresource/{res}/{id}")] HttpRequest req,
            ILogger log, ClaimsPrincipal principal, string res, string id)
        {
            FhirJsonParser _parser = new FhirJsonParser();

            _parser.Settings.AcceptUnknownMembers   = true;
            _parser.Settings.AllowUnrecognizedEnums = true;
            log.LogInformation("SecureLink Function Invoked");
            //Is the principal authenticated
            if (!principal.Identity.IsAuthenticated)
            {
                return(new ContentResult()
                {
                    Content = "User is not Authenticated", StatusCode = (int)System.Net.HttpStatusCode.Unauthorized
                });
            }
            //Is the prinicipal a FHIR Server Administrator
            ClaimsIdentity ci = (ClaimsIdentity)principal.Identity;

            if (!ci.IsInFHIRRole(Environment.GetEnvironmentVariable("ADMIN_ROLE")))
            {
                return(new ContentResult()
                {
                    Content = "User does not have suffiecient rights (Administrator required)", StatusCode = (int)System.Net.HttpStatusCode.Unauthorized
                });
            }
            string aadten = ci.Tenant();
            //Custom Headers for User Audit in FHIR
            List <HeaderParm> auditheaders = new List <HeaderParm>();

            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-USERID", principal.Identity.Name));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-TENANT", aadten));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-SOURCE", req.HttpContext.Connection.RemoteIpAddress.ToString()));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-PROXY", "FHIRProxy-LinkResource"));
            //Are we linking the correct resource type
            if (string.IsNullOrEmpty(res) || !allowedresources.Any(res.ToLower().Contains))
            {
                return(new BadRequestObjectResult("Linked resource must be Patient,Practitioner or RelatedPerson"));
            }
            string name = req.Query["name"];

            if (string.IsNullOrEmpty(name))
            {
                return(new BadRequestObjectResult("Linked resource must have principal name specified in parameters (e.g. ?name=)"));
            }
            //Get/update/check current bearer token to talk to authenticate to FHIR Server
            if (_bearerToken == null || FHIRClient.isTokenExpired(_bearerToken))
            {
                lock (_lock)
                {
                    log.LogInformation($"Obtaining new OAUTH2 Bearer Token for access to FHIR Server");
                    _bearerToken = FHIRClient.GetOAUTH2BearerToken(System.Environment.GetEnvironmentVariable("FS_TENANT_NAME"), System.Environment.GetEnvironmentVariable("FS_RESOURCE"),
                                                                   System.Environment.GetEnvironmentVariable("FS_CLIENT_ID"), System.Environment.GetEnvironmentVariable("FS_SECRET"));
                }
            }
            //Get a FHIR Client so we can talk to the FHIR Server
            log.LogInformation($"Instanciating FHIR Client Proxy");
            FHIRClient fhirClient = new FHIRClient(System.Environment.GetEnvironmentVariable("FS_URL"), _bearerToken);

            int.TryParse(System.Environment.GetEnvironmentVariable("LINK_DAYS"), out int i_link_days);
            //Load the resource to Link
            var fhirresp = fhirClient.LoadResource(res + "/" + id, null, false, auditheaders.ToArray());
            var lres     = _parser.Parse <Resource>((string)fhirresp.Content);

            if (lres.ResourceType == Hl7.Fhir.Model.ResourceType.OperationOutcome)
            {
                return(new BadRequestObjectResult(lres.ToString()));
            }
            //Add Link to AAD Tenent in Identifiers
            if (lres.ResourceType == Hl7.Fhir.Model.ResourceType.Practitioner)
            {
                var tr   = (Hl7.Fhir.Model.Practitioner)lres;
                var fbid = tr.Identifier.FirstOrDefault(ident => ident.System == aadten);
                if (fbid != null)
                {
                    tr.Identifier.Remove(fbid);
                }
                Hl7.Fhir.Model.Identifier newid = new Hl7.Fhir.Model.Identifier(aadten, name);
                newid.Period = new Hl7.Fhir.Model.Period(Hl7.Fhir.Model.FhirDateTime.Now(), new Hl7.Fhir.Model.FhirDateTime(DateTimeOffset.Now.AddDays(i_link_days)));
                tr.Identifier.Add(newid);
                FhirJsonSerializer serializer = new FhirJsonSerializer();
                string             srv        = serializer.SerializeToString(tr);
                var saveresult = fhirClient.SaveResource(Enum.GetName(typeof(ResourceType), tr.ResourceType), srv, "PUT", auditheaders.ToArray());
                if (saveresult.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    return(new OkObjectResult($"Identity: {name} in directory {aadten} is now linked to {res}/{id}"));
                }
                else
                {
                    return(new BadRequestObjectResult($"Unable to link Identity: {name} in directory {aadten}:{saveresult.StatusCode}"));
                }
            }
            else if (lres.ResourceType == Hl7.Fhir.Model.ResourceType.Patient)
            {
                var tr   = (Hl7.Fhir.Model.Patient)lres;
                var fbid = tr.Identifier.FirstOrDefault(ident => ident.System == aadten);
                if (fbid != null)
                {
                    tr.Identifier.Remove(fbid);
                }
                Hl7.Fhir.Model.Identifier newid = new Hl7.Fhir.Model.Identifier(aadten, name);
                newid.Period = new Hl7.Fhir.Model.Period(Hl7.Fhir.Model.FhirDateTime.Now(), new Hl7.Fhir.Model.FhirDateTime(DateTimeOffset.Now.AddDays(i_link_days)));
                tr.Identifier.Add(newid);
                FhirJsonSerializer serializer = new FhirJsonSerializer();
                string             srv        = serializer.SerializeToString(tr);
                var saveresult = fhirClient.SaveResource(Enum.GetName(typeof(ResourceType), tr.ResourceType), srv, "PUT", auditheaders.ToArray());
                if (saveresult.StatusCode == System.Net.HttpStatusCode.OK)
                {
                    return(new OkObjectResult($"Identity: {name} in directory {aadten} is now linked to {res}/{id}"));
                }
                else
                {
                    return(new BadRequestObjectResult($"Unable to link Identity: {name} in directory {aadten}:{saveresult.StatusCode}"));
                }
            }

            return(new OkObjectResult($"No action taken Identity: {name}"));
        }
Пример #5
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", "put", "patch", "delete", Route = "participant/{res}/{id?}")] HttpRequest req,
            [Blob("%DEIDCONFIG%", FileAccess.Read, Connection = "STORAGEACCT")] CloudBlockBlob deidconfig,
            ILogger log, ClaimsPrincipal principal, string res, string id)
        {
            log.LogInformation("FHIR SecureAccess Function Invoked");
            if (!principal.Identity.IsAuthenticated)
            {
                return(new ContentResult()
                {
                    Content = Utils.genOOErrResponse("login", "User is not Authenticated"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                });
            }
            //Is the prinicipal a FHIR Server Administrator
            ClaimsIdentity ci    = (ClaimsIdentity)principal.Identity;
            bool           admin = ci.IsInFHIRRole(Environment.GetEnvironmentVariable("ADMIN_ROLE"));

            //GET (READ)

            if (req.Method.Equals("GET"))
            {
                if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("READER_ROLE")))
                {
                    return(new ContentResult()
                    {
                        Content = Utils.genOOErrResponse("auth-denied", "User/Application must be in a reader role to access"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                    });
                }
            }
            else
            { //OTHER VERBS ARE WRITER
                if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("WRITER_ROLE")))
                {
                    return(new ContentResult()
                    {
                        Content = Utils.genOOErrResponse("auth-denied", "User/Application must be in a writer role to update"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                    });
                }
            }
            string aadten      = ci.Tenant();
            string name        = principal.Identity.Name;
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

            //Get/update/check current bearer token to talk to authemticate to FHIR Server
            if (_bearerToken == null || FHIRClient.isTokenExpired(_bearerToken))
            {
                lock (_lock)
                {
                    if (_bearerToken == null || FHIRClient.isTokenExpired(_bearerToken))
                    {
                        log.LogInformation($"Obtaining new OAUTH2 Bearer Token for access to FHIR Server");
                        _bearerToken = FHIRClient.GetOAUTH2BearerToken(System.Environment.GetEnvironmentVariable("FS_TENANT_NAME"), System.Environment.GetEnvironmentVariable("FS_RESOURCE"),
                                                                       System.Environment.GetEnvironmentVariable("FS_CLIENT_ID"), System.Environment.GetEnvironmentVariable("FS_SECRET"));
                    }
                }
            }
            //Create User Custom Headers for Audit
            List <HeaderParm> auditheaders = new List <HeaderParm>();

            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-USERID", name));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-TENANT", aadten));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-SOURCE", req.HttpContext.Connection.RemoteIpAddress.ToString()));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-PROXY", "FHIRProxy-ParticipantPatientRelationship"));
            //Preserve Relevant FHIR Headers
            List <HeaderParm> customandrestheaders = new List <HeaderParm>();

            foreach (string key in req.Headers.Keys)
            {
                string s = key.ToLower();
                if (s.Equals("etag"))
                {
                    customandrestheaders.Add(new HeaderParm(key, req.Headers[key].First()));
                }
                else if (s.StartsWith("if-"))
                {
                    customandrestheaders.Add(new HeaderParm(key, req.Headers[key]
                                                            .First()));
                }
            }
            //Add User Audit Headers
            customandrestheaders.AddRange(auditheaders);
            //Get a FHIR Client so we can talk to the FHIR Server
            log.LogInformation($"Instanciating FHIR Client Proxy");
            FHIRClient    fhirClient         = new FHIRClient(System.Environment.GetEnvironmentVariable("FS_URL"), _bearerToken);
            FHIRResponse  fhirresp           = null;
            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(","));
            //Load linked Resource Identifiers for each known role the user is in
            foreach (string r in inroles)
            {
                if (fhirresourceroles.Any(r.Equals))
                {
                    fhirresp = fhirClient.LoadResource(r, $"identifier={aadten}|{name}", true, auditheaders.ToArray());
                    var st = (JObject)fhirresp.Content;
                    if (st != null && ((string)st["resourceType"]).Equals("Bundle"))
                    {
                        JArray entries = (JArray)st["entry"];
                        foreach (JToken tok in entries)
                        {
                            resourceidentities.Add((string)tok["resource"]["resourceType"] + "/" + (string)tok["resource"]["id"]);
                        }
                    }
                }
            }
            //Proxy the call to the FHIR Server
            JObject result = null;
            Dictionary <string, bool> porcache = new Dictionary <string, bool>();

            if (req.Method.Equals("GET"))
            {
                var qs = req.QueryString.HasValue ? req.QueryString.Value : null;
                fhirresp = fhirClient.LoadResource(res + (id == null ? "" : "/" + id), qs, false, customandrestheaders.ToArray());
            }
            else
            {
                fhirresp = fhirClient.SaveResource(res, requestBody, req.Method, customandrestheaders.ToArray());
            }
            //Fix location header to proxy address
            if (fhirresp.Headers.ContainsKey("Location"))
            {
                fhirresp.Headers["Location"].Value = fhirresp.Headers["Location"].Value.Replace(Environment.GetEnvironmentVariable("FS_URL"), req.Scheme + "://" + req.Host.Value + req.Path.Value.Substring(0, req.Path.Value.IndexOf(res) - 1));
            }
            var fhirstr = fhirresp.Content == null ? "" : (string)fhirresp.Content;

            //Fix server locations to proxy address
            fhirstr = fhirstr.Replace(Environment.GetEnvironmentVariable("FS_URL"), req.Scheme + "://" + req.Host.Value + req.Path.Value.Substring(0, req.Path.Value.IndexOf(res) - 1));
            result  = JObject.Parse(fhirstr);
            //Role Checks if not Administrator
            if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("GLOBAL_ACCESS_ROLES")))
            {
                if (((string)result["resourceType"]).Equals("Bundle"))
                {
                    JArray entries  = (JArray)result["entry"];
                    JArray toremove = new JArray();
                    for (int i = entries.Count - 1; i >= 0; i--)
                    {
                        if (!IsAParticipantOrPatient((JObject)entries[i]["resource"], fhirClient, resourceidentities, porcache, auditheaders.ToArray()))
                        {
                            entries[i].Remove();
                        }
                    }
                }
                else if (!((string)result["resourceType"]).Equals("OperationalOutcome"))
                {
                    if (!IsAParticipantOrPatient(result, fhirClient, resourceidentities, porcache, auditheaders.ToArray()))
                    {
                        return(new ContentResult()
                        {
                            Content = Utils.genOOErrResponse("auth-denied", $"Not authorized to access resource:{res + (id == null ? "" : "/" + id)}"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                        });
                    }
                }
            }
            //Add Response from FHIR Server Headers
            foreach (string key in fhirresp.Headers.Keys)
            {
                req.HttpContext.Response.Headers.Remove(key);
                req.HttpContext.Response.Headers.Add(key, fhirresp.Headers[key].Value);
            }
            //DE-ID
            if (ci.IsInFHIRRole(Environment.GetEnvironmentVariable("DEID_ROLES")))
            {
                if (_deidconfig == null && deidconfig.ExistsAsync().GetAwaiter().GetResult())
                {
                    lock (_lock)
                    {
                        if (_deidconfig == null)
                        {
                            log.LogInformation($"Loading de-id config from blob store");
                            var cs = deidconfig.DownloadTextAsync().GetAwaiter().GetResult();
                            _deidconfig = AnonymizerConfigurationManager.CreateFromConfigurationString(cs);
                        }
                    }
                }
                AnonymizerEngine _engine = new AnonymizerEngine(_deidconfig);
                var str1 = _engine.AnonymizeJson(result.ToString(Formatting.None));
                result = JObject.Parse(str1);
            }
            return(new JsonResult(result));
        }
Пример #6
0
        private static bool IsAParticipantOrPatient(JObject resource, FHIRClient fhirClient, IEnumerable <string> knownresourceIdentities, Dictionary <string, bool> porcache, HeaderParm[] auditheaders)
        {
            string  patientId   = null;
            string  encounterId = null;
            JObject patient     = null;
            JObject 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 (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  = 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 = 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   = 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.Split("/")[1];
                    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 = 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);
        }
Пример #7
0
        public static async Task <IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", "put", "patch", "delete", Route = "fhir/{res?}/{id?}")] HttpRequest req,
            ILogger log, ClaimsPrincipal principal, string res, string id)
        {
            /*The basic FHIR proxy by default only validates that the user principal is authenticated (AuthN).
             * You can clone this code and add your own authorization logic, pre and post processing logic to fit your business
             * use cases. This function will accept standard REST Verbs as used by HL7 FHIR
             *
             * IMPORTANT:  Do not publish this function without Authentication (Easy Auth or APIM) you will compromise your FHIR server!
             *
             */
            log.LogInformation("FHIR ProxyBase Function Invoked");
            if (!principal.Identity.IsAuthenticated)
            {
                return(new ContentResult()
                {
                    Content = "User is not Authenticated", StatusCode = (int)System.Net.HttpStatusCode.Unauthorized
                });
            }
            /* Load the ClaimsIdentity for use in RBAC based logic */
            ClaimsIdentity ci = (ClaimsIdentity)principal.Identity;
            /* Load the tenant Name from principal */
            string aadten = ci.Tenant();
            /* Load the principal Name */
            string name = principal.Identity.Name;
            /* Minimum Authorization must be in an access Role Reader, Writer or Admin based on HTTP Verb*/
            bool admin = ci.IsInFHIRRole(Environment.GetEnvironmentVariable("ADMIN_ROLE"));

            //GET (READ)
            if (req.Method.Equals("GET"))
            {
                if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("READER_ROLE")))
                {
                    return(new ContentResult()
                    {
                        Content = Utils.genOOErrResponse("auth-access", "User/Application must be in a reader role to access"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                    });
                }
            }
            else
            { //OTHER VERBS ARE WRITER
                if (!admin && !ci.IsInFHIRRole(Environment.GetEnvironmentVariable("WRITER_ROLE")))
                {
                    return(new ContentResult()
                    {
                        Content = Utils.genOOErrResponse("auth-access", "User/Application must be in a writer role to update"), StatusCode = (int)System.Net.HttpStatusCode.Unauthorized, ContentType = "application/json"
                    });
                }
            }
            /* Load the request contents */
            string requestBody = await new StreamReader(req.Body).ReadToEndAsync();

            /* Get/update/check current bearer token to authenticate the proxy to the FHIR Server
             * The following parameters must be defined in environment variables:
             * To use Manged Service Identity or Service Client:
             * FS_URL = the fully qualified URL to the FHIR Server
             * FS_RESOURCE = the audience or resource for the FHIR Server for Azure API for FHIR should be https://azurehealthcareapis.com
             * To use a Service Client Principal the following must also be specified:
             * FS_TENANT_NAME = the GUID or UPN of the AAD tenant that is hosting FHIR Server Authentication
             * FS_CLIENT_ID = the client or app id of the private client authorized to access the FHIR Server
             * FS_SECRET = the client secret to pass to FHIR Server Authentication
             */
            if (!string.IsNullOrEmpty(System.Environment.GetEnvironmentVariable("FS_RESOURCE")) && FHIRClient.isTokenExpired(_bearerToken))
            {
                lock (_lock)
                {
                    if (FHIRClient.isTokenExpired(_bearerToken))
                    {
                        log.LogInformation($"Obtaining new OAUTH2 Bearer Token for access to FHIR Server");
                        _bearerToken = FHIRClient.GetOAUTH2BearerToken(System.Environment.GetEnvironmentVariable("FS_RESOURCE"), System.Environment.GetEnvironmentVariable("FS_TENANT_NAME"),
                                                                       System.Environment.GetEnvironmentVariable("FS_CLIENT_ID"), System.Environment.GetEnvironmentVariable("FS_SECRET")).GetAwaiter().GetResult();
                    }
                }
            }

            /*
             * Create User Custom Headers these headers are passed to the FHIR Server to communicate credentials of the authorized user for each proxy call
             * this is ensures accruate audit trails for FHIR server access. Note: This headers are honored by the Azure API for FHIR Server
             */
            List <HeaderParm> auditheaders = new List <HeaderParm>();

            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-USERID", principal.Identity.Name));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-TENANT", ci.Tenant()));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-SOURCE", req.HttpContext.Connection.RemoteIpAddress.ToString()));
            auditheaders.Add(new HeaderParm("X-MS-AZUREFHIR-AUDIT-PROXY", "FHIRProxy-ProxyBase"));
            /* Preserve FHIR Specific change control headers and include in the proxy call */
            List <HeaderParm> customandrestheaders = new List <HeaderParm>();

            foreach (string key in req.Headers.Keys)
            {
                string s = key.ToLower();
                if (s.Equals("etag"))
                {
                    customandrestheaders.Add(new HeaderParm(key, req.Headers[key].First()));
                }
                else if (s.StartsWith("if-"))
                {
                    customandrestheaders.Add(new HeaderParm(key, req.Headers[key].First()));
                }
            }
            /* Add User Audit Headers */
            customandrestheaders.AddRange(auditheaders);
            /* Get a FHIR Client instance to talk to the FHIR Server */
            log.LogInformation($"Instanciating FHIR Client Proxy");
            FHIRClient   fhirClient = new FHIRClient(System.Environment.GetEnvironmentVariable("FS_URL"), _bearerToken);
            FHIRResponse fhirresp   = null;

            /*
             * TODO: Add your pre-call Filter Logic here
             * Any custom pre FHIR filtering, security, validations
             * or transform mappings.
             */
            /* Proxy the call to the FHIR Server */
            JObject result = null;

            if (req.Method.Equals("GET"))
            {
                var qs = req.QueryString.HasValue ? req.QueryString.Value : null;
                fhirresp = fhirClient.LoadResource(res + (id == null ? "" : "/" + id), qs, false, customandrestheaders.ToArray());
            }
            else
            {
                fhirresp = fhirClient.SaveResource(res, requestBody, req.Method, customandrestheaders.ToArray());
            }
            /* Fix location header to proxy address */
            if (fhirresp.Headers.ContainsKey("Location"))
            {
                fhirresp.Headers["Location"].Value = fhirresp.Headers["Location"].Value.Replace(Environment.GetEnvironmentVariable("FS_URL"), req.Scheme + "://" + req.Host.Value + req.Path.Value.Substring(0, req.Path.Value.IndexOf(res) - 1));
            }
            var str = fhirresp.Content == null ? "" : (string)fhirresp.Content;

            /* Fix server locations to proxy address */
            str    = str.Replace(Environment.GetEnvironmentVariable("FS_URL"), req.Scheme + "://" + req.Host.Value + req.Path.Value.Substring(0, req.Path.Value.IndexOf(res) - 1));
            result = JObject.Parse(str);

            /*
             * TODO: Add your Filter Logic here
             * Any custom post FHIR filtering or security checks
             * or transform mappings.
             */
            /* Add Headers from FHIR Server Response */
            foreach (string key in fhirresp.Headers.Keys)
            {
                req.HttpContext.Response.Headers.Remove(key);
                req.HttpContext.Response.Headers.Add(key, fhirresp.Headers[key].Value);
            }
            var jr = new JsonResult(result);

            jr.StatusCode = (int)fhirresp.StatusCode;
            return(jr);
        }