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);
        }
Esempio n. 2
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
            {
                if (req.Method.Equals("DELETE"))
                {
                    fhirresp = fhirClient.DeleteResource(res + (id == null ? "" : "/" + id), 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 + (res != null ? req.Path.Value.Substring(0, req.Path.Value.IndexOf(res) - 1) : req.Path.Value));
            result = (string.IsNullOrEmpty(str) ? null : 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);
        }