keycloak-uncached

Details

diff --git a/integration/jaxrs-oauth-client/pom.xml b/integration/jaxrs-oauth-client/pom.xml
index 0e23a63..a167f99 100755
--- a/integration/jaxrs-oauth-client/pom.xml
+++ b/integration/jaxrs-oauth-client/pom.xml
@@ -70,6 +70,13 @@
             <artifactId>junit</artifactId>
             <scope>test</scope>
         </dependency>
+
+        <dependency>
+            <groupId>org.osgi</groupId>
+            <artifactId>org.osgi.core</artifactId>
+            <version>4.3.0</version>
+            <scope>provided</scope>
+        </dependency>
     </dependencies>
     <build>
         <plugins>
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
index 06602f1..33795c2 100755
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsBearerTokenFilterImpl.java
@@ -5,6 +5,7 @@ import org.keycloak.adapters.AdapterDeploymentContext;
 import org.keycloak.adapters.AdapterUtils;
 import org.keycloak.adapters.AuthChallenge;
 import org.keycloak.adapters.AuthOutcome;
+import org.keycloak.adapters.AuthenticatedActionsHandler;
 import org.keycloak.adapters.BearerTokenRequestAuthenticator;
 import org.keycloak.adapters.KeycloakConfigResolver;
 import org.keycloak.adapters.KeycloakDeployment;
@@ -43,17 +44,17 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
 
     private String keycloakConfigFile;
     private String keycloakConfigResolverClass;
-    private volatile boolean started;
+    protected volatile boolean started;
 
     protected AdapterDeploymentContext deploymentContext;
 
-    // TODO: Should also handle stop lifecycle for de-registration
+    // TODO: Should also somehow handle stop lifecycle for de-registration
     protected NodesRegistrationManagement nodesRegistrationManagement;
     protected UserSessionManagement userSessionManagement = new EmptyUserSessionManagement();
 
     public void setKeycloakConfigFile(String configFile) {
         this.keycloakConfigFile = configFile;
-        start();
+        attemptStart();
     }
 
     public String getKeycloakConfigFile() {
@@ -66,7 +67,25 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
 
     public void setKeycloakConfigResolverClass(String keycloakConfigResolverClass) {
         this.keycloakConfigResolverClass = keycloakConfigResolverClass;
-        start();
+        attemptStart();
+    }
+
+    // INITIALIZATION AND STARTUP
+
+    protected void attemptStart() {
+        if (started) {
+            throw new IllegalStateException("Filter already started. Make sure to specify just keycloakConfigResolver or keycloakConfigFile but not both");
+        }
+
+        if (isInitialized()) {
+            start();
+        } else {
+            log.fine("Not yet initialized");
+        }
+    }
+
+    protected boolean isInitialized() {
+        return this.keycloakConfigFile != null || this.keycloakConfigResolverClass != null;
     }
 
     protected void start() {
@@ -75,30 +94,20 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         }
 
         if (keycloakConfigResolverClass != null) {
-            Class<?> clazz;
-            try {
-                clazz = getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
-            } catch (ClassNotFoundException cnfe) {
-                // Fallback to tccl
-                try {
-                    clazz = Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
-                } catch (ClassNotFoundException cnfe2) {
-                    throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
-                }
-            }
+            Class<? extends KeycloakConfigResolver> resolverClass = loadResolverClass();
 
             try {
-                KeycloakConfigResolver resolver = (KeycloakConfigResolver) clazz.newInstance();
+                KeycloakConfigResolver resolver = resolverClass.newInstance();
                 log.info("Using " + resolver + " to resolve Keycloak configuration on a per-request basis.");
                 this.deploymentContext = new AdapterDeploymentContext(resolver);
             } catch (Exception e) {
-                throw new RuntimeException("Unable to instantiate resolver " + clazz);
+                throw new RuntimeException("Unable to instantiate resolver " + resolverClass);
             }
         } else {
             if (keycloakConfigFile == null) {
                 throw new IllegalArgumentException("You need to specify either keycloakConfigResolverClass or keycloakConfigFile in configuration");
             }
-            InputStream is = readConfigFile();
+            InputStream is = loadKeycloakConfigFile();
             KeycloakDeployment kd = KeycloakDeploymentBuilder.build(is);
             deploymentContext = new AdapterDeploymentContext(kd);
             log.info("Keycloak is using a per-deployment configuration loaded from: " + keycloakConfigFile);
@@ -108,7 +117,20 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         started = true;
     }
 
-    protected InputStream readConfigFile() {
+    protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
+        try {
+            return (Class<? extends KeycloakConfigResolver>)getClass().getClassLoader().loadClass(keycloakConfigResolverClass);
+        } catch (ClassNotFoundException cnfe) {
+            // Fallback to tccl
+            try {
+                return (Class<? extends KeycloakConfigResolver>)Thread.currentThread().getContextClassLoader().loadClass(keycloakConfigResolverClass);
+            } catch (ClassNotFoundException cnfe2) {
+                throw new RuntimeException("Unable to find resolver class: " + keycloakConfigResolverClass);
+            }
+        }
+    }
+
+    protected InputStream loadKeycloakConfigFile() {
         if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
             String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
             log.fine("Loading config from classpath on location: " + classPathLocation);
@@ -135,11 +157,13 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         }
     }
 
+    // REQUEST HANDLING
+
     @Override
     public void filter(ContainerRequestContext request) throws IOException {
         SecurityContext securityContext = getRequestSecurityContext(request);
         JaxrsHttpFacade facade = new JaxrsHttpFacade(request, securityContext);
-        if (handlePreauth(request, facade)) {
+        if (handlePreauth(facade)) {
             return;
         }
 
@@ -150,12 +174,12 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         bearerAuthentication(facade, request, resolvedDeployment);
     }
 
-    protected boolean handlePreauth(ContainerRequestContext request, JaxrsHttpFacade facade) {
+    protected boolean handlePreauth(JaxrsHttpFacade facade) {
         PreAuthActionsHandler handler = new PreAuthActionsHandler(userSessionManagement, deploymentContext, facade);
         if (handler.handleRequest()) {
-            // Response might be already finished if error was sent
+            // Send response now (if not already sent)
             if (!facade.isResponseFinished()) {
-                request.abortWith(facade.getResponseBuilder().build());
+                facade.getResponse().end();
             }
             return true;
         }
@@ -175,9 +199,9 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
                 facade.getResponse().setStatus(Response.Status.UNAUTHORIZED.getStatusCode());
             }
 
-            // Send response now
+            // Send response now (if not already sent)
             if (!facade.isResponseFinished()) {
-                request.abortWith(facade.getResponseBuilder().build());
+                facade.getResponse().end();
             }
             return;
         } else {
@@ -187,6 +211,7 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         }
 
         propagateSecurityContext(facade, request, resolvedDeployment, bearer);
+        handleAuthActions(facade, resolvedDeployment);
     }
 
     protected void propagateSecurityContext(JaxrsHttpFacade facade, ContainerRequestContext request, KeycloakDeployment resolvedDeployment, BearerTokenRequestAuthenticator bearer) {
@@ -239,6 +264,16 @@ public class JaxrsBearerTokenFilterImpl implements JaxrsBearerTokenFilter {
         return request.getSecurityContext();
     }
 
+    protected void handleAuthActions(JaxrsHttpFacade facade, KeycloakDeployment deployment) {
+        AuthenticatedActionsHandler authActionsHandler = new AuthenticatedActionsHandler(deployment, facade);
+        if (authActionsHandler.handledRequest()) {
+            // Send response now (if not already sent)
+            if (!facade.isResponseFinished()) {
+                facade.getResponse().end();
+            }
+        }
+    }
+
     // We don't have any sessions to manage with pure jaxrs filter
     private static class EmptyUserSessionManagement implements UserSessionManagement {
 
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
index df72fce..f29935d 100644
--- a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/JaxrsHttpFacade.java
@@ -136,8 +136,9 @@ public class JaxrsHttpFacade implements HttpFacade {
 
         @Override
         public void end() {
-            // For now doesn't need to be supported
-            throw new IllegalStateException("Not supported yet");
+            javax.ws.rs.core.Response response = responseBuilder.build();
+            requestContext.abortWith(response);
+            responseFinished = true;
         }
     }
 
@@ -168,8 +169,4 @@ public class JaxrsHttpFacade implements HttpFacade {
     public boolean isResponseFinished() {
         return responseFinished;
     }
-
-    public javax.ws.rs.core.Response.ResponseBuilder getResponseBuilder() {
-        return responseFacade.responseBuilder;
-    }
 }
diff --git a/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/OsgiJaxrsBearerTokenFilterImpl.java b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/OsgiJaxrsBearerTokenFilterImpl.java
new file mode 100644
index 0000000..2c2f4b0
--- /dev/null
+++ b/integration/jaxrs-oauth-client/src/main/java/org/keycloak/jaxrs/OsgiJaxrsBearerTokenFilterImpl.java
@@ -0,0 +1,78 @@
+package org.keycloak.jaxrs;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.logging.Logger;
+
+import javax.annotation.Priority;
+import javax.ws.rs.Priorities;
+import javax.ws.rs.container.PreMatching;
+
+import org.keycloak.adapters.KeycloakConfigResolver;
+import org.keycloak.constants.GenericConstants;
+import org.osgi.framework.BundleContext;
+
+/**
+ * Variant of JaxrsBearerTokenFilter, which can be used to properly use resources from current osgi bundle
+ *
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+@PreMatching
+@Priority(Priorities.AUTHENTICATION)
+public class OsgiJaxrsBearerTokenFilterImpl extends JaxrsBearerTokenFilterImpl {
+
+    private final static Logger log = Logger.getLogger("" + JaxrsBearerTokenFilterImpl.class);
+
+    private BundleContext bundleContext;
+
+    public BundleContext getBundleContext() {
+        return bundleContext;
+    }
+
+    public void setBundleContext(BundleContext bundleContext) {
+        this.bundleContext = bundleContext;
+        attemptStart();
+    }
+
+    @Override
+    protected boolean isInitialized() {
+        return super.isInitialized() && bundleContext != null;
+    }
+
+    @Override
+    protected Class<? extends KeycloakConfigResolver> loadResolverClass() {
+        String resolverClass = getKeycloakConfigResolverClass();
+        try {
+            return (Class<? extends KeycloakConfigResolver>) bundleContext.getBundle().loadClass(resolverClass);
+        } catch (ClassNotFoundException cnfe) {
+            log.warning("Not able to find class from bundleContext. Fallback to current classloader");
+            return super.loadResolverClass();
+        }
+    }
+
+    @Override
+    protected InputStream loadKeycloakConfigFile() {
+        String keycloakConfigFile = getKeycloakConfigFile();
+        if (keycloakConfigFile.startsWith(GenericConstants.PROTOCOL_CLASSPATH)) {
+
+            // Load from classpath of current bundle
+            String classPathLocation = keycloakConfigFile.replace(GenericConstants.PROTOCOL_CLASSPATH, "");
+            log.fine("Loading config from classpath on location: " + classPathLocation);
+
+            URL cfgUrl = bundleContext.getBundle().getResource(classPathLocation);
+            if (cfgUrl == null) {
+                log.warning("Not able to find configFile from bundleContext. Fallback to current classloader");
+                return super.loadKeycloakConfigFile();
+            }
+
+            try {
+                return cfgUrl.openStream();
+            } catch (IOException ioe) {
+                throw new RuntimeException(ioe);
+            }
+        } else {
+            return super.loadKeycloakConfigFile();
+        }
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsFilterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsFilterTest.java
index 10554c5..936792d 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsFilterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/jaxrs/JaxrsFilterTest.java
@@ -20,6 +20,7 @@ import org.junit.Test;
 import org.junit.rules.ExternalResource;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.TokenIdGenerator;
+import org.keycloak.adapters.CorsHeaders;
 import org.keycloak.constants.AdapterConstants;
 import org.keycloak.adapters.HttpClientBuilder;
 import org.keycloak.models.ApplicationModel;
@@ -228,6 +229,52 @@ public class JaxrsFilterTest {
     }
 
     @Test
+    public void testCors() {
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                Map<String,String> initParams = new TreeMap<String,String>();
+                initParams.put(CONFIG_FILE_INIT_PARAM, "classpath:jaxrs-test/jaxrs-keycloak.json");
+                keycloakRule.deployJaxrsApplication("JaxrsSimpleApp", "/jaxrs-simple", JaxrsTestApplication.class, initParams);
+            }
+
+        });
+
+        // Send OPTIONS request
+        Response optionsResp = client.target(JAXRS_APP_URL).request()
+                .header(CorsHeaders.ORIGIN, "http://localhost:8081")
+                .options();
+        Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
+        Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
+        optionsResp.close();
+
+        // Retrieve token
+        OAuthClient.AccessTokenResponse accessTokenResp = retrieveAccessToken();
+        String authHeader = "Bearer " + accessTokenResp.getAccessToken();
+
+        // Send GET request with token but bad origin
+        Response badOriginResp = client.target(JAXRS_APP_URL).request()
+                .header(HttpHeaders.AUTHORIZATION, authHeader)
+                .header(CorsHeaders.ORIGIN, "http://evil.org")
+                .get();
+        Assert.assertEquals(403, badOriginResp.getStatus());
+        badOriginResp.close();
+
+        // Send GET request with token and good origin
+        Response goodResp = client.target(JAXRS_APP_URL).request()
+                .header(HttpHeaders.AUTHORIZATION, authHeader)
+                .header(CorsHeaders.ORIGIN, "http://localhost:8081")
+                .get();
+        Assert.assertEquals(200, goodResp.getStatus());
+        Assert.assertEquals("true", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS));
+        Assert.assertEquals("http://localhost:8081", optionsResp.getHeaderString(CorsHeaders.ACCESS_CONTROL_ALLOW_ORIGIN));
+        JaxrsTestResource.SimpleRepresentation getRep = goodResp.readEntity(JaxrsTestResource.SimpleRepresentation.class);
+        Assert.assertEquals("get", getRep.getMethod());
+        goodResp.close();
+    }
+
+    @Test
     public void testPushNotBefore() {
         keycloakRule.update(new KeycloakRule.KeycloakSetup() {
 
diff --git a/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak.json b/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak.json
index 31e97d1..5a83668 100644
--- a/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak.json
+++ b/testsuite/integration/src/test/resources/jaxrs-test/jaxrs-keycloak.json
@@ -4,5 +4,6 @@
     "realm-public-key": "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCrVrCuTtArbgaZzL1hvh0xtL5mc7o0NqPVnYXkLvgcwiC3BjLGw1tGEGoJaXDuSaRllobm53JBhjx33UNv+5z/UMG4kytBWxheNVKnL6GgqlNabMaFfPLPCF8kAgKnsi79NMo+n6KnSY8YeUmec/p2vjO2NjsSAVcWEQMVhJ31LwIDAQAB",
     "auth-server-url": "http://localhost:8081/auth",
     "ssl-required" : "external",
-    "bearer-only": true
+    "bearer-only": true,
+    "enable-cors": true
 }
\ No newline at end of file