From 16322dfe70578bf2abc6a65a65b8173746740934 Mon Sep 17 00:00:00 2001 From: Bashir Sadjad Date: Fri, 29 Nov 2024 00:51:25 +0000 Subject: [PATCH] many changes to concepts and getting_started plus other minor fixes --- README.md | 4 +- doc/docs/concepts.md | 196 +++++++++++++++++---------- doc/docs/design.md | 10 +- doc/docs/getting_started.md | 95 +++++++------ doc/{ => docs/images}/flow.png | Bin doc/{ => docs/images}/integrated.png | Bin doc/{ => docs/images}/separate.png | Bin doc/docs/{ => images}/summary.png | Bin doc/docs/index.md | 55 +++++--- doc/docs/tutorial_docker.md | 4 + doc/mkdocs.yml | 1 + 11 files changed, 232 insertions(+), 133 deletions(-) rename doc/{ => docs/images}/flow.png (100%) rename doc/{ => docs/images}/integrated.png (100%) rename doc/{ => docs/images}/separate.png (100%) rename doc/docs/{ => images}/summary.png (100%) diff --git a/README.md b/README.md index b0d62fad..b887f4a3 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ The requests to the access proxy should have the access token as a Bearer Authorization header. Based on that, the proxy decides whether to grant access for a FHIR query. -Modules involved in FHIR authorization/access-control +Modules involved in FHIR authorization/access-control For more information on the technical design, [see the design doc](doc/docs/design.md). @@ -177,7 +177,7 @@ accessing the proxy from an Android emulator, which runs on a separate network. GCP note: if this is not on a VM with proper service account (e.g., on a local host), you need to pass GCP credentials to it, for example by mapping the `.config/gcloud` volume (i.e., add `-v ~/.config/gcloud:/root/.config/gcloud` to -the above command). +the docker command). # How to use this proxy diff --git a/doc/docs/concepts.md b/doc/docs/concepts.md index 7b043ba6..41848328 100644 --- a/doc/docs/concepts.md +++ b/doc/docs/concepts.md @@ -1,96 +1,156 @@ # Concepts ## Common Terminologies -These are some common terminologies that are important when dealing with access control in general -| Term | Description | -| -------------- | -----------| -| Authentication and Identity Provider (IDP) | Who the user is; establishing the identity. Can be done through a shared service (e.g., “sign in with” Google, GitHub) | -| Authorization | Given the identity, what can a user access? Has context specific pieces (e.g., scopes) | -| Access-control | How to make sure users access authorized resources only. The **core focus of the info gateway** | -| oAuth2.0 | A standard to grant access to an application on behalf of a user | -| SMART-on-FHIR | Defines a workflow that an application can use to securely request access to FHIR data, and then receive and use that data | +These are some common terminologies that are important when dealing with access +control in general: + +| Term | Description | +|---------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------| +| Authentication and Identity Provider (IDP) | Who the user is. Establishing the identity can be done through a shared service (e.g., Google, GitHub) or a special one. | +| Authorization (AuthZ) | Given the identity, what can a user access? Has context specific pieces (e.g., scopes) | +| Access-control | How to make sure users access authorized resources only. This is the **core focus of the info gateway**. | +| Client app | An app which needs to access FHIR resources on behalf of a user. | +| User | The user that is using the app; this is the identity being "authenticated". | +| Access Token | A JWT that is provided as a Bearer token when accessing FHIR resources. | +| OAuth2.0 | A standard to grant access to an application on behalf of a user. | +| [SMART-on-FHIR](https://hl7.org/fhir/smart-app-launch/) | Defines workflows that an application can use to securely request and access FHIR data. | + +The following picture helps to visualise the relationship of these concepts. +A client app (e.g., a SMART-on-FHIR app) should first use a process to fetch an +_"access token"_ from the IDP+AuthZ service. For example, this process might +be OAuth's +[Authorization Code Flow](https://auth0.com/docs/get-started/authentication-and-authorization-flow/authorization-code-flow). +This token is then provided on each request to the access-control gateway when +accessing FHIR resources. + +![FHIR Info Gateway](images/Info_Gateway_Overview.png) ## Info Gateway Modules -The Info Gateway consists of a core, which is in the [server](https://github.com/google/fhir-gateway/tree/main/server) module, and a set of _access-checker_ plugins, which can be implemented by third parties and added to the proxy server. Two sample plugins are implemented in the [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) module. +The Info Gateway consists of a core, which is in +the [`server`](https://github.com/google/fhir-gateway/tree/main/server) module, +and a set of _access-checker_ plugins, which can be implemented by third parties +and added to the proxy server. Two sample plugins are implemented in the +[`plugins`](https://github.com/google/fhir-gateway/tree/main/plugins) module. -There is also a sample `exec` module which shows how all pieces can be woven together into a single Spring Boot app. +There is also a sample +[`exec`](https://github.com/google/fhir-gateway/tree/main/exec) module which +shows how all pieces can be woven together into a single Spring Boot app. It +also has examples for implementing custom end-points. -**Notes:** +**Notes:** -* [1]Spring Boot is not a requirement for using FHIR Info Gateway; we just use it to simplify the [MainApp](exec/src/main/java/com/google/fhir/gateway/MainApp.java). +* [1] Spring Boot is not a requirement for using FHIR Info Gateway; we just use + it to simplify the + [MainApp](https://github.com/google/fhir-gateway/tree/main/exec/src/main/java/com/google/fhir/gateway/MainApp.java). * [2] The only Spring-related requirement is to do a -[@ComponentScan](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html) -to find all access-checker plugins in the classpath. + [@ComponentScan](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/ComponentScan.html) + to find all access-checker plugins in the classpath. ## Configuration parameters The configuration parameters are provided through environment variables: -- `PROXY_TO`: The base url of the FHIR store e.g.: - +- `PROXY_TO`: The base url of the FHIR server e.g.: ```shell export PROXY_TO=https://example.com/fhir ``` - `TOKEN_ISSUER`: The URL of the access token issuer, e.g.: - ```shell export TOKEN_ISSUER=http://localhost:9080/auth/realms/test ``` - - The above example is based on the default config of a test IDP+AuthZ [Keycloak](https://github.com/Alvearie/keycloak-extensions-for-fhir) server. - To see how this server is configured, check the [docker/keycloak](docker/https://github.com/google/fhir-gateway/tree/main/docker/keycloak) directory. If you want to use a SMART-on-FHIR app use this realm instead: - + The above example is based on the default config of a test IDP+AuthZ + [Keycloak](https://www.keycloak.org/) server. To see how this server is + configured, check the + [docker/keycloak](docker/https://github.com/google/fhir-gateway/tree/main/docker/keycloak) + directory. If you want to use a SMART-on-FHIR (SoF) app use this realm instead + which is based on Keycloak's + [SoF extension](https://github.com/Alvearie/keycloak-extensions-for-fhir): ```shell export TOKEN_ISSUER=http://localhost:9080/auth/realms/test-smart ``` -- `ACCESS_CHECKER`: The access-checker to use. Each access-checker has a name (see [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) for details) and this variable should be set to the name of the plugin to use. For example, to use one of the sample plugins include one of: - +- `ACCESS_CHECKER`: The access-checker to use. Each access-checker has a name + (see [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) for + details) and this variable should be set to the name of the plugin to use. For + example, to use one of the sample plugins use one of: ```shell export ACCESS_CHECKER=list export ACCESS_CHECKER=patient ``` + For more information on how access-checkers work and building your own, + see [section on access checkers](#access-checkers). - For more information on how access-checkers work and building your own, see [section on access checkers](#access-checkers). - -- `ALLOWED_QUERIES_FILE`: A list of URL requests that should bypass the access checker and always be allowed. +- `ALLOWED_QUERIES_FILE`: A list of URL requests that should bypass the access + checker and always be allowed. [`AllowedQueriesChecker`](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/AllowedQueriesChecker.java) - compares the incoming request with a configured set of allowed-queries. The intended use of this checker is to override all other access-checkers for certain user-defined criteria. The user defines their criteria in a config file and if the URL query matches an entry in the config file, access is granted. [AllowedQueriesConfig](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/AllowedQueriesConfig.java) provides all the supported configurations. An example of this is + compares the incoming request with a configured set of allowed-queries. The + intended use of this checker is to override all other access-checkers for + certain user-defined criteria. The user defines their criteria in a config + file and if the URL query matches an entry in the config file, access is + granted. [`AllowedQueriesConfig`](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/AllowedQueriesConfig.java) + shows all the supported configurations. An example config file is [`hapi_page_url_allowed_queries.json`](https://github.com/google/fhir-gateway/blob/main/resources/hapi_page_url_allowed_queries.json). - To use this file with `ALLOWED_QUERIES_FILE`: - ```shell export ALLOWED_QUERIES_FILE="resources/hapi_page_url_allowed_queries.json" ``` -- `BACKEND_TYPE`: The type of backend, either `HAPI` or `GCP`. `HAPI` should be used for most FHIR servers, while `GCP` should be used for GCP FHIR stores. +- `BACKEND_TYPE`: The type of backend, either `HAPI` or `GCP`. `HAPI` should be + used for most FHIR servers, while `GCP` should be used for GCP FHIR stores. ## Access Checkers -FHIR Info Gateway uses _access checker plugins_ to define the logic it uses to make decisions for access requests. Most users should create an access checker plugin to implement the access control logic for a specific use case. You can learn about access checker plugins by looking at the `List` access checker plugin example (provided). -See tutorial on [creating an access checker plugin](tutorial_first_access_checker.md) +FHIR Info Gateway uses _access checker plugins_ to define the logic it uses to +make decisions for access requests. Most users should create an access checker +plugin to implement the access control logic for a specific use case. You can +learn about access checker plugins by looking at the sample access checker +[plugins](https://github.com/google/fhir-gateway/tree/main/plugins/src/main/java/com/google/fhir/gateway/plugin). + +See tutorial on +[creating an access checker plugin](tutorial_first_access_checker.md). The core +of FHIR Info Gateway, provides libraries that make it easier to create +access-checkers. For example, +[PatientFinder](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/PatientFinder.java) +can be used to limit access to a certain set of patients. + +### Patient access checker plugin +The [`PatientAccessChecker` plugin](https://github.com/google/fhir-gateway/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/PatientAccessChecker.java) +can be used if the client is a SMART-on-FHIR app that uses the +[standalone app launch flow](https://www.hl7.org/fhir/smart-app-launch/app-launch.html#launch-app-standalone-launch). +It expects a `patient_id` claim in the access-token and limits access to FHIR +resources that belong to that patient. It supports +[SoF scopes](https://www.hl7.org/fhir/smart-app-launch/scopes-and-launch-context.html#scopes-for-requesting-clinical-data) +(both v1 and v2). ### Explore the List access checker plugin -The [`ListAccessChecker` plugin](https://github.com/google/fhir-access-proxy/blob/main/plugins/src/main/java/com/google/fhir/proxy/plugin/ListAccessChecker.java) is a simple example of list-based access control. It works by assigning each user a [FHIR List resource](https://www.hl7.org/fhir/list.html) which contains a list of references of `Patient` resources that the user should have access to. When a client makes a request to FHIR Information Gateway, the `ListAccessChecker` grants access if the request contains or references at least one `Patient` on that user's Patient access list. - -The plugin expects the patient list resource's ID to be included as the value to a claim named `patient_list` in the decoded JWT of the access token used to authorize requests to the FHIR Information Gateway server. For example, following the [test Docker deployment](https://github.com/google/fhir-access-proxy/wiki/Try-out-FHIR-Information-Gateway) you may get a decoded access token like the following: - +The [`ListAccessChecker` plugin](https://github.com/google/fhir-gateway/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/ListAccessChecker.java) +is a simple example of list-based access control. It works by assigning each +user a [FHIR `List` resource](https://www.hl7.org/fhir/list.html) which contains +a list of references of `Patient` resources that the user should have access to. +When a client makes a request to FHIR Information Gateway, +the `ListAccessChecker` grants access if all patients that are referenced in +the query are on the user's patient access list. + +The plugin expects the patient `List` resource's ID to be included as the value +to a claim named `patient_list` in the access token used to authorize requests +to the FHIR Information Gateway server. For example, following the +[test Docker deployment](https://github.com/google/fhir-access-proxy/wiki/Try-out-FHIR-Information-Gateway) +you may get a decoded JWT like the following (note if you use the default +settings you will get more claims that are not relevant to the access-checker +logic; so they are removed in this example): ```json { - "header": - { + "header": { "alg": "RS256", "typ": "JWT", "kid": "MnXk25Vp_W6X_UMi4sA3_iEMwuumZkwhOuE8eMY8LFo" }, - "payload": - { + "payload": { "exp": 1673990497, "iat": 1673990197, "jti": "5bb2b1a0-e9c6-442f-abfd-a22f1798fd11", @@ -101,43 +161,41 @@ The plugin expects the patient list resource's ID to be included as the value to "azp": "my-fhir-client", "session_state": "967e82a2-0188-4774-abbc-6bb4ce26536f", "acr": "1", - "realm_access": - { - "roles": - [ - "default-roles-test", - "offline_access", - "uma_authorization" - ] - }, - "resource_access": - { - "account": - { - "roles": - [ - "manage-account", - "manage-account-links", - "view-profile" - ] - } - }, "scope": "email profile", "sid": "967e82a2-0188-4774-abbc-6bb4ce26536f", "email_verified": false, "patient_list": "patient-list-example", "preferred_username": "testuser", - "group": - [ + "group": [ "fhirUser" ] } } ``` - -Here `patient_list` equals `patient-list-example`, so if your FHIR server is at `http://localhost:8099/fhir/` then this client's patient access list resource is `http://localhost:8099/fhir/List/patient-list-example`. - -The decoded JWT is passed to your `AccessCheckerFactory` implementation's `create()` function. The `ListAccessChecker` implementation extracts the patient list ID from the JWT and saves it internally. Custom JWT claims in the access token can be a good way to pass additional information to your access checker beyond what your authentication server provides. - -`ListAccessChecker`'s `checkAccess` function splits access logic according to the HTTP method. Simple yes/no access decisions like `processGet()` use the [`NoOpAccessDecision`](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/NoOpAccessDecision.java) class which you may also use in your own implementations. Alternatively, you may have more complex decision needs, such as doing additional processing after the data access like `processPost()`. In this case, implement your own version of [`AccessDecision`](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java). The `ListAccessChecker` allows clients to create new `Patient` resources without restriction (always allow access), and then as a post-processing step adds the new Patient id to the client's patient access list. You can see this implemented in [`AccessGrantedAndUpdateList`](https://github.com/google/fhir-access-proxy/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/AccessGrantedAndUpdateList.java). +Here `patient_list` equals `patient-list-example`, so if your FHIR server is +at `http://localhost:8099/fhir/` then this user's patient access list resource +is `http://localhost:8099/fhir/List/patient-list-example`. + +The decoded JWT is passed to the +[`AccessCheckerFactory`](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessCheckerFactory.java) +implementation's `create()` function. The `ListAccessChecker` implementation +extracts the patient list ID from the JWT and saves it internally. Custom JWT +claims in the access token can be a good way to pass additional information to +your access checker beyond what your authentication server provides. + +`ListAccessChecker`'s +[`checkAccess`](https://github.com/google/fhir-gateway/blob/19447d7152804d2b790f22cc44ad3b1ca21c7040/plugins/src/main/java/com/google/fhir/gateway/plugin/ListAccessChecker.java#L157) +function splits access logic according to the HTTP method. Simple yes/no access +decisions like `processGet()` use the +[`NoOpAccessDecision`](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/NoOpAccessDecision.java) +class which you may also use in your own implementations. Alternatively, you may +have more complex decision needs, such as doing additional processing after the +data access like +[`processPost()`](https://github.com/google/fhir-gateway/blob/19447d7152804d2b790f22cc44ad3b1ca21c7040/plugins/src/main/java/com/google/fhir/gateway/plugin/ListAccessChecker.java#L202). +In this case, implement your own version of +[`AccessDecision`](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java). +The `ListAccessChecker` allows clients to create new `Patient` resources without +restriction (always allow access), and then as a post-processing step adds the +new Patient id to the client's patient access list. You can see this implemented +in [`AccessGrantedAndUpdateList`](https://github.com/google/fhir-access-proxy/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/AccessGrantedAndUpdateList.java). diff --git a/doc/docs/design.md b/doc/docs/design.md index 7f47e551..80f43bae 100644 --- a/doc/docs/design.md +++ b/doc/docs/design.md @@ -21,7 +21,7 @@ For more context, please see the [Introduction](#introduction) section. Here is a summary of proposals and decisions in this design document, for the first version (V1) of the FHIR gateway: - +![High-level Architecture](images/summary.png) - There are three main components responsible for access decisions: an Identity Provider (IDP) which authenticates the user, an Authorization server (AuthZ) @@ -198,8 +198,6 @@ sometimes referred to as the Identity and Access Management (IAM) or simply We also have an "access gateway" which is responsible for processing any access request for the FHIR server and is the main focus of this design doc. - - ### Authentication and authorization flow Before the user can access data, their identity should be established and based @@ -226,7 +224,7 @@ to use [PKCE](https://auth0.com/docs/authorization/flows/authorization-code-flow-with-proof-key-for-code-exchange-pkce) which is not shown in this diagram)[^1]: - +![flow](images/flow.png) Here is a brief description of each step: @@ -265,7 +263,7 @@ to AuthZ which in turn maps the user identity to some access rules to FHIR resources. Popular IAM servers like [Keycloak](https://www.keycloak.org/), support separate/3rd party IDPs. - +![separate](images/separate.png) **Integrated AuthZ+Gateway** @@ -277,7 +275,7 @@ simplifies the deployment architecture significantly, however we note that the elimination of the access token might be incompatible with some use-cases like SoF apps. - +![integrated](images/integrated.png) ## Access gateway diff --git a/doc/docs/getting_started.md b/doc/docs/getting_started.md index 08638eb2..de113993 100644 --- a/doc/docs/getting_started.md +++ b/doc/docs/getting_started.md @@ -2,83 +2,92 @@ !!! tip "Quick Start Guide" - The easiest way to get started is to follow the ["Run the Info Gateway in Docker" guide](getting_started_docker.md). + The easiest way to get started is to follow the ["Run the Info Gateway in Docker" guide](tutorial_docker.md). ## Building from source -The proxy consists of a core, which is in the [server](https://github.com/google/fhir-gateway/tree/main/server) module, and a set of _access-checker_ plugins, which can be implemented by third parties and added to the proxy server. Two sample plugins are implemented in the [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) module. -There is also a sample `exec` module which shows how all pieces can be woven together into a single Spring Boot app. - -To build all modules, from the root run: +To build all modules, from the root run (if you are a developer, do _not_ use +`spotless.apply.skip=true`): ```shell mvn package -Dspotless.apply.skip=true ``` -The server and the plugins can be run together through this executable jar ( -`--server.port` is just one of the many default Spring Boot flags): +The `server` and `plugins` modules can be run together through this executable +jar (`--server.port` is just one of the many default Spring Boot flags): ```shell java -jar exec/target/exec-0.1.0.jar --server.port=8081 ``` -Note that extra access-checker plugins can be added through the `loader.path` property (although it is probably easier to build them into your server): +Note that extra access-checker plugins can be added through the `loader.path` +property (although it is probably easier to build them into your server): ```shell java -Dloader.path="PATH-TO-ADDITIONAL-PLUGINGS/custom-plugins.jar" \ -jar exec/target/exec-0.1.0.jar --server.port=8081 ``` -The plugin library can be swapped with any third party access-checker as described in the [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) directory. Learn more about [AccessCheckers](concepts.md#access-checkers) - +The plugin library can be swapped with any third party access-checker as +described in +the [plugins](https://github.com/google/fhir-gateway/tree/main/plugins) +directory. Learn more about [AccessCheckers](concepts.md#access-checkers). ## Gateway to server access -The proxy must be able to send FHIR queries to the FHIR server. The FHIR server must be configured to accept connections from the proxy while rejecting most other requests. -If you use a [GCP FHIR store](https://cloud.google.com/healthcare-api/docs/concepts/fhir) you have the following options: +The proxy must be able to send FHIR queries to the FHIR server. The FHIR server +must be configured to accept connections from the proxy while rejecting most +other requests. -- If you have access to the FHIR store, you can use your own credentials by - doing [application-default login](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login). This is useful when testing the proxy on your local machine, and you have access to the FHIR server through your credentials. +If you use +a [GCP FHIR store](https://cloud.google.com/healthcare-api/docs/concepts/fhir) +you have the following options: -- Use a service account with required access (e.g., "Healthcare FHIR Resource Reader", "Healthcare Dataset Viewer", "Healthcare FHIR Store Viewer"). You can then run the proxy in the same GCP project on a VM with this service account. +- If you have access to the FHIR store, you can use your own credentials by + doing [application-default login](https://cloud.google.com/sdk/gcloud/reference/auth/application-default/login). + This is useful when testing the proxy on your local machine, and you have + access to the FHIR server through your credentials. -- [not-recommended] You can create and download a key file for the above service account, then use it with: +- Use a service account with required access (e.g., "Healthcare FHIR Resource + Reader", "Healthcare Dataset Viewer", "Healthcare FHIR Store Viewer"). You can + then run the proxy in the same GCP project on a VM with this service account. +- [not-recommended] You can create and download a key file for the above service + account, then use it with: ```shell export GOOGLE_APPLICATION_CREDENTIALS="PATH_TO_THE_JSON_KEY_FILE" ``` -## Running the proxy server -Once you have set all the above, you can run the proxy server. The sample `exec` module uses [Apache Tomcat](https://tomcat.apache.org/) through [Spring Boot](https://spring.io/projects/spring-boot) and the usual configuration parameters apply, e.g., to run on port 8081: +## Running the gateway -```shell -java -jar exec/target/exec-0.1.0.jar --server.port=8081 -``` +!!! tip "Configuration Parameters" -Note if the `TOKEN_ISSUER` is on the `localhost` you may need to bypass proxy's token issuer check by setting `RUN_MODE=DEV` environment variable if you are accessing the proxy from an Android emulator, which runs on a separate network. + Take a moment to review the [configuration parameters](concepts.md#configuration-parameters). -[Try the proxy with test servers in Docker](https://github.com/google/fhir-gateway/wiki/Try-out-FHIR-Information-Gateway). - -GCP note: if this is not on a VM with proper service account (e.g., on a localhost), you need to pass GCP credentials to it, for example by mapping the `.config/gcloud` volume (i.e., add `-v ~/.config/gcloud:/root/.config/gcloud` to the above command). - -## As a docker image -The proxy is also available as a docker image: +Once you have set all the above, you can run the proxy server. The sample `exec` +module uses [Apache Tomcat](https://tomcat.apache.org/) +through [Spring Boot](https://spring.io/projects/spring-boot) and the usual +configuration parameters apply, e.g., to run on port 8081: ```shell -$ docker run -p 8081:8080 -e TOKEN_ISSUER=[token_issuer_url] \ - -e PROXY_TO=[fhir_server_url] -e ACCESS_CHECKER=list \ - us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:latest +java -jar exec/target/exec-0.1.0.jar --server.port=8081 ``` -Note if the TOKEN_ISSUER is on the localhost you may need to bypass proxy's token issuer check by setting RUN_MODE=DEV environment variable if you are accessing the proxy from an Android emulator, which runs on a separate network. +!!! tip "Android Emulator" -Try the proxy with test servers in Docker, see the [Getting Started with Docker tutorial](getting_started_docker.md) - -GCP note: if this is not on a VM with proper service account (e.g., on a local host), you need to pass GCP credentials to it, for example by mapping the .config/gcloud volume (i.e., add -v ~/.config/gcloud:/root/.config/gcloud to the above command). + If the `TOKEN_ISSUER` is on the `localhost` and you are accessing the gateway from an Android emulator (which runs on a separate network), you may need to bypass proxy's token issuer check by setting `RUN_MODE=DEV` environment variable. ## Using the Info Gateway -Once the proxy is running, we first need to fetch an access token from the `TOKEN_ISSUER`; you need the test `username` and `password` plus the -`client_id`: + +In this section we assume that a Keycloak instance is set up using the sample +docker setup provided +[here](https://github.com/google/fhir-gateway/blob/main/docker/keycloak/config-compose.yaml). +If you have a different IDP+AuthZ setup, you need to adjust the parameters +below accordingly. + +Once the gateway is running, we first need to fetch an access token from +the `TOKEN_ISSUER`; you need the test server's `username` and `password` plus +the `client_id`: ```shell $ curl -X POST -d 'client_id=CLIENT_ID' -d 'username=testuser' \ @@ -86,7 +95,10 @@ $ curl -X POST -d 'client_id=CLIENT_ID' -d 'username=testuser' \ "http://localhost:9080/auth/realms/test/protocol/openid-connect/token" ``` -We need the `access_token` of the returned JSON to be able to convince the proxy to authorize our FHIR requests (there is also a `refresh_token` in the above response). Assuming this is stored in the `ACCESS_TOKEN` environment variable, we can access the FHIR store: +We need the `access_token` of the returned JSON to be able to convince the +gateway to authorize our FHIR requests (there is also a `refresh_token` in the +above response). Assuming this is stored in the `ACCESS_TOKEN` environment +variable, we can access the FHIR store: ```shell $ curl -X GET -H "Authorization: Bearer ${ACCESS_TOKEN}" \ @@ -101,9 +113,12 @@ $ curl -X PUT -H "Authorization: Bearer ${ACCESS_TOKEN}" \ -d @Patient_f16b5191-af47-4c5a-b9ca-71e0a4365824_modified.json ``` -Of course, whether a query is accepted or denied, depends on the access-checker used and the `ACCESS_TOKEN` claims. +Of course, whether a query is accepted or denied, depends on the access-checker +used and the `ACCESS_TOKEN` claims. For example: -* For `ACCESS_CHECKER=list` there should be a `patient_list` claim which is the ID of a `List` FHIR resource with all the patients that this user has access to. +* For `ACCESS_CHECKER=list` there should be a `patient_list` claim which is the + ID of a `List` FHIR resource with all the patients that this user has access + to. * For `ACCESS_CHECKER=patient`, there should be a `patient_id` claim with a valid Patient resource ID. \ No newline at end of file diff --git a/doc/flow.png b/doc/docs/images/flow.png similarity index 100% rename from doc/flow.png rename to doc/docs/images/flow.png diff --git a/doc/integrated.png b/doc/docs/images/integrated.png similarity index 100% rename from doc/integrated.png rename to doc/docs/images/integrated.png diff --git a/doc/separate.png b/doc/docs/images/separate.png similarity index 100% rename from doc/separate.png rename to doc/docs/images/separate.png diff --git a/doc/docs/summary.png b/doc/docs/images/summary.png similarity index 100% rename from doc/docs/summary.png rename to doc/docs/images/summary.png diff --git a/doc/docs/index.md b/doc/docs/index.md index 4b883c89..cebbaf8f 100644 --- a/doc/docs/index.md +++ b/doc/docs/index.md @@ -1,32 +1,55 @@ # FHIR Info Gateway -The Info Gateway is a reverse proxy which controls client access to FHIR resources on a server by checking requests for authorization to a FHIR URL or search query. -It makes it easier for developers to enforce organizational role based access control (RBAC) policies when working with FHIR data. - -* The Info Gateway enables authorization (AT) and access-control (ACL) between a client application and a FHIR server when used along with any OpenID Connect compliant Identity Provider (IdP) and Authorization server (AuthZ). -* It currently supports Keycloak as the IDP+AuthZ provider and has been tested with HAPI FHIR and Google Cloud Healthcare API FHIR store as the FHIR server. +The Info Gateway is a reverse proxy which controls client access to FHIR +resources on a server. It works by inspecting FHIR requests and verifying that +the client is authorized to access the requested resources. + +It makes it easier for developers to enforce various forms of authorization +policies including organizational role based access control (RBAC) policies +when working with FHIR data. + +* To enable authorization and access-control (ACL) policy enforcement between a + client application and a FHIR server, the Info Gateway is used along with an + Identity Provider (IDP) and Authorization server (AuthZ). +* The IDP can be a generic OpenID Connect (OIDC) compliant service or a special + purpose one. +* The IDP+AuthZ should provide a JSON Web Token (JWT) to the client. The client + uses this as a Bearer access-token (AT) when sending FHIR requests. +* A sample end-to-end implementation with Keycloak as the IDP+AuthZ service is + provided and has been tested with HAPI FHIR and Google Cloud Healthcare + FHIR-store as the FHIR server. ![FHIR Info Gateway](images/Info_Gateway_Overview.png) ## Key Features -Key features of the Info Gateway features include: +Key features of the Info Gateway include: -* A stand-alone service that can work with any FHIR compliant servers (e.g., a HAPI FHIR server, GCP FHIR store, etc.) -* A pluggable architecture for defining an access-checkers to allow for implementation configurability -* Query filtering to block/allow specific queries such as for disabling joins +* A stand-alone service that can work with any FHIR compliant servers. +* A pluggable architecture for defining an access-checker to allow for + implementation configurability. +* Query filtering to block/allow specific queries. +* Post-processing of the results returned by the FHIR-server, for example to + remove sensitive information. +* A generic interface for implementing custom endpoints, e.g., a sync endpoint + to return updates for all patients assigned to a health-worker. ## Common use cases -The Info Gateway is designed to solve for a generic problem, that is, access control for **any client**. - -Common use cases include: -1. Web based dashboard used by program admins +The Info Gateway is designed to solve for a generic problem, that is, access +control for **any client** and **any FHIR server**. -2. For a mobile app used by commnunity based frontline health workers possibly with offline support +Common access-check use-cases include: -3. For a personal health record app used by patients or care-givers +1. For a mobile app used by community based front-line health workers possibly + with offline support +2. Web based dashboard used by program admins +3. For a personal health record app used by patients or caregivers +4. To enable SMART-on-FHIR apps for patient or system level scopes -4. To enable SMART-on-FHIR for patient or system level scopes +FHIR Info Gateway is implemented as a "FHIR facade", i.e., it is a FHIR server +itself which is implemented using the +[HAPI FHIR Plain Server](https://hapifhir.io/hapi-fhir/docs/server_plain/introduction.html) +library: ![FHIR Info Gateway](images/Info_Gateway_Use_Cases.png) diff --git a/doc/docs/tutorial_docker.md b/doc/docs/tutorial_docker.md index d3c4d688..a8edca3e 100644 --- a/doc/docs/tutorial_docker.md +++ b/doc/docs/tutorial_docker.md @@ -68,6 +68,10 @@ running on your local machine. and [`patient`](https://github.com/google/fhir-gateway/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/PatientAccessChecker.java) example access-checkers. +!!! tip "GCP Note" + + If the FHIR server is GCP FHIR-store and the gateway is not run on a VM with proper service account (e.g., running on a localhost), you need to pass GCP credentials to it, for example by mapping the `.config/gcloud` volume (i.e., add `-v ~/.config/gcloud:/root/.config/gcloud` to the above command). + ## Examine the sample Keycloak configuration In this section you will review the Keycloak settings relevant to use FHIR diff --git a/doc/mkdocs.yml b/doc/mkdocs.yml index 994578d9..d37a3704 100644 --- a/doc/mkdocs.yml +++ b/doc/mkdocs.yml @@ -52,6 +52,7 @@ nav: - Tutorials: - 'Run the Info Gateway in Docker' : 'tutorial_docker.md' - 'Create an access checker' : 'tutorial_first_access_checker.md' + - Design: 'design.md' - Community: - 'Support' : 'support.md' - 'Contributing': 'contribute.md'