diff --git a/Dockerfile b/Dockerfile old mode 100644 new mode 100755 index 69c3e3df..26746167 --- a/Dockerfile +++ b/Dockerfile @@ -15,8 +15,8 @@ # # Image for building and running tests against the source code of -# the FHIR Access Proxy. -FROM maven:3.8.5-openjdk-11 as build +# the FHIR Gateway. +FROM maven:3.8.5-openjdk-11-slim as build RUN apt-get update && apt-get install -y nodejs npm RUN npm cache clean -f && npm install -g n && n stable @@ -37,10 +37,10 @@ RUN mvn spotless:check RUN mvn --batch-mode package -Pstandalone-app -Dlicense.skip=true -# Image for FHIR Access Proxy binary with configuration knobs as environment vars. +# Image for FHIR Gateway binary with configuration knobs as environment vars. FROM eclipse-temurin:11-jdk-focal as main -COPY --from=build /app/exec/target/exec-0.2.1-SNAPSHOT.jar / +COPY --from=build /app/exec/target/fhir-gateway-exec.jar / COPY resources/hapi_page_url_allowed_queries.json resources/hapi_page_url_allowed_queries.json ENV PROXY_PORT=8080 @@ -54,4 +54,4 @@ ENV BACKEND_TYPE="HAPI" ENV ACCESS_CHECKER="list" ENV RUN_MODE="PROD" -ENTRYPOINT java -jar exec-0.2.1-SNAPSHOT.jar --server.port=${PROXY_PORT} +ENTRYPOINT java -jar fhir-gateway-exec.jar --server.port=${PROXY_PORT} diff --git a/README.md b/README.md index 0e4f03a2..162cc118 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ The proxy is also available as a [docker image](Dockerfile): ```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-access-proxy:latest + us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:latest ``` Note if the `TOKEN_ISSUER` is on the `localhost` you may need to bypass proxy's diff --git a/build.sh b/build.sh index 8b3bbc3b..268c10b8 100755 --- a/build.sh +++ b/build.sh @@ -28,4 +28,4 @@ set -e export BUILD_ID=${KOKORO_BUILD_ID:-local} gcloud auth configure-docker us-docker.pkg.dev ./e2e-test/e2e.sh -docker push us-docker.pkg.dev/fhir-proxy-build/stable/fhir-access-proxy:${BUILD_ID} +docker push us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:${BUILD_ID} diff --git a/doc/design.md b/doc/design.md index 04cfe6d6..0f9a9b5f 100644 --- a/doc/design.md +++ b/doc/design.md @@ -320,35 +320,35 @@ varies by context. Each of these approaches are described in the following sections. In each case, we briefly describe what is supported in the first release of the access gateway. The "first release" is when we open-sourced the project in June 2022 in -[this GitHub repository](https://github.com/google/fhir-access-proxy). Let's +[this GitHub repository](https://github.com/google/fhir-gateway). Let's first look at the architecture of the gateway. There are two main components: -**[Server](https://github.com/google/fhir-access-proxy/tree/main/server/src/main/java/com/google/fhir/gateway)**: +**[Server](https://github.com/google/fhir-gateway/tree/main/server/src/main/java/com/google/fhir/gateway)**: The core of the access gateway is the "server" which provides a -[servlet](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java) +[servlet](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java) that processes FHIR queries and an -[authorization interceptor](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/BearerAuthorizationInterceptor.java) +[authorization interceptor](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/BearerAuthorizationInterceptor.java) that inspects those. The interceptor decodes and validates the JWT access-token and makes a call to an -[AccessChecker](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessChecker.java) +[AccessChecker](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessChecker.java) plugin to decide whether access should be granted or not. The server also provides common FHIR query/resource processing, e.g., -[PatientFinder](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/PatientFinder.java) +[PatientFinder](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/PatientFinder.java) for finding patient context. These libraries are meant to be used in the plugin implementations. -**[AccessChecker plugin](https://github.com/google/fhir-access-proxy/tree/main/plugins)**: +**[AccessChecker plugin](https://github.com/google/fhir-gateway/tree/main/plugins)**: Each access gateway needs at least one AccessChecker plugin. Gateway implementers can provide their customized access-check logic in this plugin. The server code's initialization finds plugins by looking for -[AccessCheckerFactory](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessCheckerFactory.java) +[AccessCheckerFactory](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessCheckerFactory.java) implementations that are [@Named](https://docs.oracle.com/javaee/7/api/javax/inject/Named.html). The specified name is used to select that plugin at runtime. Example implementations are -[ListAccessChecker](https://github.com/google/fhir-access-proxy/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/ListAccessChecker.java) +[ListAccessChecker](https://github.com/google/fhir-gateway/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/ListAccessChecker.java) and -[PatientAccessChecker](https://github.com/google/fhir-access-proxy/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/PatientAccessChecker.java). +[PatientAccessChecker](https://github.com/google/fhir-gateway/blob/main/plugins/src/main/java/com/google/fhir/gateway/plugin/PatientAccessChecker.java). AccessChecker plugins can send RPCs to other backends if they need to collect extra information. In our examples, the plugins consult with the same FHIR store that resources are pulled from, but you could imagine consulting more hardened @@ -374,7 +374,7 @@ This approach helps support both the **flexible-access-control** and **untrusted-app** items from the [constraints](#scenarios-and-constraints) section. Note to use this approach for access-control, the patient context should be inferred from the FHIR query. The server provides -[a library](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/PatientFinderImp.java) +[a library](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/PatientFinderImp.java) for doing this. ### Query templates allowed/blocked list @@ -394,7 +394,7 @@ search results of a previous query. Just from these queries, we cannot decide what the patient context is, so we should let those queries go through (there is a security risk here but since `_getpages` param values are ephemeral UUIDs, this is probably ok). Here is a -[sample config](https://github.com/google/fhir-access-proxy/blob/main/resources/hapi_page_url_allowed_queries.json) +[sample config](https://github.com/google/fhir-gateway/blob/main/resources/hapi_page_url_allowed_queries.json) for this. We note that we want our core "server" to be _stateless_ (for easy scalability); therefore cannot store next/prev URLs from previous query results. @@ -424,11 +424,11 @@ structure of FHIR queries that the gateway accepts). So we still need some restrictions on the permitted queries as mentioned above. Among gateway interfaces, there is -[AccessDecision](https://github.com/google/fhir-access-proxy/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java) +[AccessDecision](https://github.com/google/fhir-gateway/blob/main/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java) which is returned from a -[checkAccess](https://github.com/google/fhir-access-proxy/blob/85f7c87a26494d4efba5d01904c8c27074eb26a9/server/src/main/java/com/google/fhir/gateway/interfaces/AccessChecker.java#L31). +[checkAccess](https://github.com/google/fhir-gateway/blob/85f7c87a26494d4efba5d01904c8c27074eb26a9/server/src/main/java/com/google/fhir/gateway/interfaces/AccessChecker.java#L31). This interface has a -[postProcess](https://github.com/google/fhir-access-proxy/blob/85f7c87a26494d4efba5d01904c8c27074eb26a9/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java#L39) +[postProcess](https://github.com/google/fhir-gateway/blob/85f7c87a26494d4efba5d01904c8c27074eb26a9/server/src/main/java/com/google/fhir/gateway/interfaces/AccessDecision.java#L39) method which can be used for post-processing of resources returned from the FHIR server. diff --git a/docker/hapi-proxy-compose.yaml b/docker/hapi-proxy-compose.yaml index bbcbf53e..9e511c38 100644 --- a/docker/hapi-proxy-compose.yaml +++ b/docker/hapi-proxy-compose.yaml @@ -35,7 +35,7 @@ version: "3.0" services: fhir-proxy: - image: us-docker.pkg.dev/fhir-proxy-build/stable/fhir-access-proxy:${BUILD_ID:-latest} + image: us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:${BUILD_ID:-latest} environment: - TOKEN_ISSUER - PROXY_TO diff --git a/e2e-test/e2e.sh b/e2e-test/e2e.sh index 5f4d1b66..724b6a28 100755 --- a/e2e-test/e2e.sh +++ b/e2e-test/e2e.sh @@ -21,7 +21,7 @@ set -e export BUILD_ID=${KOKORO_BUILD_ID:-local} function setup() { - docker build -t us-docker.pkg.dev/fhir-proxy-build/stable/fhir-access-proxy:${BUILD_ID} . + docker build -t us-docker.pkg.dev/fhir-proxy-build/stable/fhir-gateway:${BUILD_ID} . docker-compose -f docker/keycloak/config-compose.yaml \ up --force-recreate --remove-orphans -d --quiet-pull # TODO find a way to expose docker container logs in the output; currently diff --git a/exec/pom.xml b/exec/pom.xml old mode 100644 new mode 100755 index 472f74d0..a5ae4600 --- a/exec/pom.xml +++ b/exec/pom.xml @@ -79,6 +79,9 @@ + + ${project.parent.artifactId}-${project.artifactId} + diff --git a/plugins/pom.xml b/plugins/pom.xml old mode 100644 new mode 100755 diff --git a/pom.xml b/pom.xml old mode 100644 new mode 100755 diff --git a/server/pom.xml b/server/pom.xml old mode 100644 new mode 100755 diff --git a/server/src/main/java/com/google/fhir/gateway/AllowedQueriesChecker.java b/server/src/main/java/com/google/fhir/gateway/AllowedQueriesChecker.java old mode 100644 new mode 100755 diff --git a/server/src/main/java/com/google/fhir/gateway/AllowedQueriesConfig.java b/server/src/main/java/com/google/fhir/gateway/AllowedQueriesConfig.java old mode 100644 new mode 100755 diff --git a/server/src/main/java/com/google/fhir/gateway/BearerAuthorizationInterceptor.java b/server/src/main/java/com/google/fhir/gateway/BearerAuthorizationInterceptor.java old mode 100644 new mode 100755 diff --git a/server/src/main/java/com/google/fhir/gateway/ExceptionUtil.java b/server/src/main/java/com/google/fhir/gateway/ExceptionUtil.java index 90778d97..5edb826e 100644 --- a/server/src/main/java/com/google/fhir/gateway/ExceptionUtil.java +++ b/server/src/main/java/com/google/fhir/gateway/ExceptionUtil.java @@ -53,7 +53,7 @@ static void throwRuntimeExceptionAndLog(Logger logger, String errorMessage) { throwRuntimeExceptionAndLog(logger, errorMessage, null, RuntimeException.class); } - static void throwRuntimeExceptionAndLog(Logger logger, String errorMessage, Exception e) { + public static void throwRuntimeExceptionAndLog(Logger logger, String errorMessage, Exception e) { throwRuntimeExceptionAndLog(logger, errorMessage, e, RuntimeException.class); } } diff --git a/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java b/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java index bf44d67d..5ab61d20 100644 --- a/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java +++ b/server/src/main/java/com/google/fhir/gateway/FhirProxyServer.java @@ -17,6 +17,7 @@ import ca.uhn.fhir.context.FhirContext; import ca.uhn.fhir.rest.api.Constants; +import ca.uhn.fhir.rest.server.ApacheProxyAddressStrategy; import ca.uhn.fhir.rest.server.RestfulServer; import ca.uhn.fhir.rest.server.interceptor.CorsInterceptor; import com.google.fhir.gateway.interfaces.AccessCheckerFactory; @@ -46,7 +47,7 @@ public class FhirProxyServer extends RestfulServer { // Spring's automatic scanning. @Autowired private Map accessCheckerFactories; - static boolean isDevMode() { + public static boolean isDevMode() { String runMode = System.getenv("RUN_MODE"); return "DEV".equals(runMode); } @@ -77,6 +78,8 @@ protected void initialize() throws ServletException { } catch (IOException e) { ExceptionUtil.throwRuntimeExceptionAndLog(logger, "IOException while initializing", e); } + + setServerAddressStrategy(new ApacheProxyAddressStrategy(true)); } private AccessCheckerFactory chooseAccessCheckerFactory() { diff --git a/server/src/main/java/com/google/fhir/gateway/ProxyConstants.java b/server/src/main/java/com/google/fhir/gateway/ProxyConstants.java index edb50b64..9077ac49 100644 --- a/server/src/main/java/com/google/fhir/gateway/ProxyConstants.java +++ b/server/src/main/java/com/google/fhir/gateway/ProxyConstants.java @@ -19,7 +19,7 @@ import org.apache.http.entity.ContentType; public class ProxyConstants { - // Note we should not set charset here; otherwise GCP FHIR store complains about Content-Type. static final ContentType JSON_PATCH_CONTENT = ContentType.create(Constants.CT_JSON_PATCH); + public static final String HTTP_URL_SEPARATOR = "/"; } diff --git a/server/src/test/java/com/google/fhir/gateway/AllowedQueriesCheckerTest.java b/server/src/test/java/com/google/fhir/gateway/AllowedQueriesCheckerTest.java old mode 100644 new mode 100755 diff --git a/server/src/test/java/com/google/fhir/gateway/BearerAuthorizationInterceptorTest.java b/server/src/test/java/com/google/fhir/gateway/BearerAuthorizationInterceptorTest.java index 466ef318..f6448350 100644 --- a/server/src/test/java/com/google/fhir/gateway/BearerAuthorizationInterceptorTest.java +++ b/server/src/test/java/com/google/fhir/gateway/BearerAuthorizationInterceptorTest.java @@ -62,16 +62,11 @@ import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.junit.MockitoJUnitRunner; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; import org.springframework.mock.web.MockHttpServletResponse; @RunWith(MockitoJUnitRunner.class) public class BearerAuthorizationInterceptorTest { - private static final Logger logger = - LoggerFactory.getLogger(BearerAuthorizationInterceptorTest.class); - private static final FhirContext fhirContext = FhirContext.forR4(); private BearerAuthorizationInterceptor testInstance;