Skip to content

Commit

Permalink
issue #2777 - bulkdata authz enforcement in fhir-smart
Browse files Browse the repository at this point in the history
1. Introduce beforeInvoke and afterInvoke to the
FHIRPersistenceInterceptor interface. I wanted these to take the
FHIROperationContext which is in fhir-server, so that lead to:

2. Move the FHIRPersistenceInterceptor (and related classes) from
fhir-persistence to fhir-server (where they are called from).
Unfortunately, fhir-notification implements FHIRPersistenceInterceptor,
but fhir-server depends on fhir-notification. I tried breaking this
dependency but it was difficult because fhir-server currently
initializes the websocket notification impl differently than the kafka
and nats ones. So that lead to:

3. Move fhir-notification and all 3 implementations into fhir-server. At
first I didn't like this, but this was the easiest way out of the
dependency mess. If we want to break them back out, we should introduce
a proper notification spi. This should get revisited when we get to
#1289

4. Finally, I made the change I actually wanted to make, which is to add
support for `system/[resourceType].[read|write]` scopes in fhir-smart
and check these scopes before we allow requests to `$export`, `$import`,
and `$bulkdata-status`.

Signed-off-by: Lee Surprenant <lmsurpre@us.ibm.com>
  • Loading branch information
lmsurpre committed Sep 21, 2021
1 parent 7593bd5 commit c824e60
Show file tree
Hide file tree
Showing 45 changed files with 1,812 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -56,11 +56,11 @@
import com.ibm.fhir.persistence.FHIRPersistence;
import com.ibm.fhir.persistence.context.FHIRPersistenceContext;
import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;
import com.ibm.fhir.persistence.exception.FHIRPersistenceResourceDeletedException;
import com.ibm.fhir.persistence.helper.FHIRPersistenceHelper;
import com.ibm.fhir.persistence.helper.FHIRTransactionHelper;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.validation.exception.FHIRValidationException;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
import com.ibm.fhir.config.FHIRRequestContext;
import com.ibm.fhir.model.resource.Resource;
import com.ibm.fhir.notification.exception.FHIRNotificationException;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException;
import com.ibm.fhir.persistence.interceptor.impl.FHIRPersistenceInterceptorMgr;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@

package com.ibm.fhir.persistence.context;

import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.search.context.FHIRSearchContext;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@
import com.ibm.fhir.core.FHIRConstants;
import com.ibm.fhir.persistence.context.impl.FHIRHistoryContextImpl;
import com.ibm.fhir.persistence.context.impl.FHIRPersistenceContextImpl;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.search.context.FHIRSearchContext;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* SPDX-License-Identifier: Apache-2.0
*/

package com.ibm.fhir.persistence.interceptor;
package com.ibm.fhir.persistence.context;

import java.util.HashMap;
import java.util.Map;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import com.ibm.fhir.persistence.context.FHIRHistoryContext;
import com.ibm.fhir.persistence.context.FHIRPersistenceContext;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.search.context.FHIRSearchContext;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,22 @@

package com.ibm.fhir.persistence.interceptor;

import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;

/**
* This interface describes a persistence interceptor. Persistence interceptors are invoked by the FHIR Server to allow
* users to inject business logic into the REST API processing flow. To make use of this interceptor, develop a class
* that implements this interface, then store your implementation class name in a file called
* META-INF/services/com.ibm.fhir.persistence.FHIRPersistenceInterceptor within your jar file.
* @deprecated
*/
@Deprecated
public interface FHIRPersistenceInterceptor {

/**
* This method is called during the processing of a 'create' REST API invocation, immediately before the new
* resource is stored by the persistence layer.
*
*
* @param event
* information about the 'create' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -28,7 +32,7 @@ default void beforeCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInte
/**
* This method is called during the processing of a 'create' REST API invocation, immediately after the new resource
* has been stored by the persistence layer.
*
*
* @param event
* information about the 'create' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -39,7 +43,7 @@ default void afterCreate(FHIRPersistenceEvent event) throws FHIRPersistenceInter
/**
* This method is called during the processing of an 'update' REST API invocation, immediately before the updated
* resource is stored by the persistence layer.
*
*
* @param event
* information about the 'update' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -50,18 +54,18 @@ default void beforeUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInte
/**
* This method is called during the processing of an 'update' REST API invocation, immediately after the updated
* resource has been stored by the persistence layer.
*
*
* @param event
* information about the 'update' event
* @throws FHIRPersistenceInterceptorException
*/
default void afterUpdate(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException {
}

/**
* This method is called during the processing of an 'patch' REST API invocation, immediately before the updated
* resource is stored by the persistence layer.
*
*
* @param event
* information about the 'patch' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -72,7 +76,7 @@ default void beforePatch(FHIRPersistenceEvent event) throws FHIRPersistenceInter
/**
* This method is called during the processing of an 'patch' REST API invocation, immediately after the updated
* resource has been stored by the persistence layer.
*
*
* @param event
* information about the 'patch' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -81,9 +85,9 @@ default void afterPatch(FHIRPersistenceEvent event) throws FHIRPersistenceInterc
}

/**
* This method is called during the processing of a 'delete' REST API invocation, immediately before the
* This method is called during the processing of a 'delete' REST API invocation, immediately before the
* resource is deleted by the persistence layer.
*
*
* @param event
* information about the 'delete' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -94,18 +98,18 @@ default void beforeDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInte
/**
* This method is called during the processing of a 'delete' REST API invocation, immediately after the
* resource has been deleted by the persistence layer.
*
*
* @param event
* information about the 'delete' event
* @throws FHIRPersistenceInterceptorException
*/
default void afterDelete(FHIRPersistenceEvent event) throws FHIRPersistenceInterceptorException {
}

/**
* This method is called during the processing of a 'read' REST API invocation, immediately before the resource is
* read by the persistence layer.
*
*
* @param event
* information about the 'read' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -116,7 +120,7 @@ default void beforeRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterc
/**
* This method is called during the processing of a 'read' REST API invocation, immediately after the resource has
* been read by the persistence layer.
*
*
* @param event
* information about the 'read' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -127,7 +131,7 @@ default void afterRead(FHIRPersistenceEvent event) throws FHIRPersistenceInterce
/**
* This method is called during the processing of a 'vread' (versioned read) REST API invocation, immediately before
* the resource is read by the persistence layer.
*
*
* @param event
* information about the 'vread' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -138,7 +142,7 @@ default void beforeVread(FHIRPersistenceEvent event) throws FHIRPersistenceInter
/**
* This method is called during the processing of a 'vread' REST API invocation, immediately after the resource has
* been read by the persistence layer.
*
*
* @param event
* information about the 'vread' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -149,7 +153,7 @@ default void afterVread(FHIRPersistenceEvent event) throws FHIRPersistenceInterc
/**
* This method is called during the processing of a 'history' REST API invocation, immediately before the resource's
* history is read by the persistence layer.
*
*
* @param event
* information about the 'history' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -160,7 +164,7 @@ default void beforeHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInt
/**
* This method is called during the processing of a 'history' REST API invocation, immediately after the resource's
* history has been read by the persistence layer.
*
*
* @param event
* information about the 'history' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -171,7 +175,7 @@ default void afterHistory(FHIRPersistenceEvent event) throws FHIRPersistenceInte
/**
* This method is called during the processing of a 'search' REST API invocation, immediately before the search is
* performed by the persistence layer.
*
*
* @param event
* information about the 'search' event
* @throws FHIRPersistenceInterceptorException
Expand All @@ -182,7 +186,7 @@ default void beforeSearch(FHIRPersistenceEvent event) throws FHIRPersistenceInte
/**
* This method is called during the processing of a 'search' REST API invocation, immediately after the search has
* been performed by the persistence layer.
*
*
* @param event
* information about the 'search' event
* @throws FHIRPersistenceInterceptorException
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@
import com.ibm.fhir.model.resource.OperationOutcome;
import com.ibm.fhir.persistence.exception.FHIRPersistenceException;

/**
* @deprecated moved to com.ibm.fhir.server.interceptor in fhir-server
*/
@Deprecated
public class FHIRPersistenceInterceptorException extends FHIRPersistenceException {
private static final long serialVersionUID = 1L;

Expand All @@ -27,11 +31,11 @@ public FHIRPersistenceInterceptorException withIssue(OperationOutcome.Issue... i
super.withIssue(issues);
return this;
}

@Override
public FHIRPersistenceInterceptorException withIssue(Collection<OperationOutcome.Issue> issues) {
super.withIssue(issues);
return this;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
import java.util.logging.Logger;

import com.ibm.fhir.core.FHIRUtilities;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException;

Expand All @@ -28,7 +28,9 @@
* and then insert your implementation class name into a file called
* META-INF/services/com.ibm.fhir.persistence.FHIRPersistenceInterceptor and store that file in your jar.
* These "interceptor" jars should be stored in a common place defined by the FHIR Server.
* @deprecated moved to com.ibm.fhir.server.interceptor in fhir-server
*/
@Deprecated
public class FHIRPersistenceInterceptorMgr {
private static final Logger log = Logger.getLogger(FHIRPersistenceInterceptorMgr.class.getName());

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@
import com.ibm.fhir.persistence.context.FHIRHistoryContext;
import com.ibm.fhir.persistence.context.FHIRPersistenceContext;
import com.ibm.fhir.persistence.context.FHIRPersistenceContextFactory;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.impl.FHIRPersistenceContextImpl;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.search.context.FHIRSearchContext;
import com.ibm.fhir.search.context.FHIRSearchContextFactory;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

import com.ibm.fhir.model.resource.Patient;
import com.ibm.fhir.model.test.TestUtil;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;

/**
* Tests associated with the FHIRPersistenceContextImpl class.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import com.ibm.fhir.model.type.Coding;
import com.ibm.fhir.model.type.Meta;
import com.ibm.fhir.model.type.Uri;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceEvent;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptor;
import com.ibm.fhir.persistence.interceptor.FHIRPersistenceInterceptorException;
import com.ibm.fhir.persistence.context.FHIRPersistenceEvent;
import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptor;
import com.ibm.fhir.server.interceptor.FHIRPersistenceInterceptorException;

/**
* A sample persistence interceptor that adds a tag to each resource before it gets persisted.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,8 @@

import org.glassfish.tyrus.core.CloseReasons;

import com.ibm.fhir.notification.FHIRNotificationEvent;
import com.ibm.fhir.notification.util.FHIRNotificationUtil;
import com.ibm.fhir.server.notification.FHIRNotificationEvent;
import com.ibm.fhir.server.notification.FHIRNotificationUtil;

public class FHIRNotificationServiceClientEndpoint extends Endpoint {
private boolean DEBUG = false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
import com.ibm.fhir.model.resource.Observation;
import com.ibm.fhir.model.resource.Patient;
import com.ibm.fhir.model.test.TestUtil;
import com.ibm.fhir.notification.FHIRNotificationEvent;
import com.ibm.fhir.server.notification.FHIRNotificationEvent;
import com.ibm.fhir.server.test.FHIRServerTestBase;

/**
Expand Down
42 changes: 25 additions & 17 deletions fhir-server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,18 @@
<artifactId>jakarta.websocket-api</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.kafka</groupId>
<artifactId>kafka-clients</artifactId>
</dependency>
<dependency>
<groupId>io.nats</groupId>
<artifactId>jnats</artifactId>
</dependency>
<dependency>
<groupId>io.nats</groupId>
<artifactId>java-nats-streaming</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-validation</artifactId>
Expand Down Expand Up @@ -85,41 +97,37 @@
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-notification</artifactId>
<artifactId>fhir-provider</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-notification-websocket</artifactId>
<artifactId>fhir-persistence-jdbc</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-notification-kafka</artifactId>
<version>${project.version}</version>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-notification-nats</artifactId>
<version>${project.version}</version>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-provider</artifactId>
<version>${project.version}</version>
<artifactId>fhir-examples</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>${project.groupId}</groupId>
<artifactId>fhir-persistence-jdbc</artifactId>
<artifactId>fhir-model</artifactId>
<version>${project.version}</version>
<type>test-jar</type>
</dependency>
<dependency>
<groupId>org.owasp.encoder</groupId>
<artifactId>encoder</artifactId>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<groupId>org.skyscreamer</groupId>
<artifactId>jsonassert</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.glassfish.tyrus.bundles</groupId>
Expand Down
Loading

0 comments on commit c824e60

Please sign in to comment.