keycloak-memoizeit

Merge pull request #2990 from trex667/feat/keycloak-1733 [KEYCLOAK-1733]:

7/20/2016 5:46:11 AM

Details

diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
index 420ae93..a58a05a 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/OIDCAuthenticationError.java
@@ -35,7 +35,8 @@ public class OIDCAuthenticationError implements AuthenticationError {
         CODE_TO_TOKEN_FAILURE,
         INVALID_TOKEN,
         STALE_TOKEN,
-        NO_AUTHORIZATION_HEADER
+        NO_AUTHORIZATION_HEADER,
+        NO_QUERY_PARAMETER_ACCESS_TOKEN
     }
 
     private Reason reason;
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java
new file mode 100644
index 0000000..5ee6662
--- /dev/null
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/QueryParamterTokenRequestAuthenticator.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016 Red Hat, Inc. and/or its affiliates
+ * and other contributors as indicated by the @author tags.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.keycloak.adapters;
+
+import org.jboss.logging.Logger;
+import org.keycloak.adapters.spi.AuthOutcome;
+import org.keycloak.adapters.spi.HttpFacade;
+
+/**
+ * @author <a href="mailto:froehlich.ch@gmail.com">Christian Froehlich</a>
+ * @version $Revision: 1 $
+ */
+public class QueryParamterTokenRequestAuthenticator extends BearerTokenRequestAuthenticator {
+    public static final String ACCESS_TOKEN = "access_token";
+    protected Logger log = Logger.getLogger(QueryParamterTokenRequestAuthenticator.class);
+
+    public QueryParamterTokenRequestAuthenticator(KeycloakDeployment deployment) {
+        super(deployment);
+    }
+
+    public AuthOutcome authenticate(HttpFacade exchange) {
+        tokenString = null;
+        tokenString = getAccessTokenFromQueryParamter(exchange);
+        if (tokenString == null || tokenString.trim().isEmpty()) {
+            challenge = challengeResponse(exchange, OIDCAuthenticationError.Reason.NO_QUERY_PARAMETER_ACCESS_TOKEN, null, null);
+            return AuthOutcome.NOT_ATTEMPTED;
+        }
+        return (authenticateToken(exchange, tokenString));
+    }
+
+    String getAccessTokenFromQueryParamter(HttpFacade exchange) {
+        try {
+            if (exchange != null && exchange.getRequest() != null) {
+                return exchange.getRequest().getQueryParamValue(ACCESS_TOKEN);
+            }
+        } catch (Exception ignore) {
+        }
+        return null;
+    }
+}
diff --git a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
index 2cd1261..c59087c 100755
--- a/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
+++ b/adapters/oidc/adapter-core/src/main/java/org/keycloak/adapters/RequestAuthenticator.java
@@ -61,7 +61,7 @@ public abstract class RequestAuthenticator {
         if (log.isTraceEnabled()) {
             log.trace("try bearer");
         }
-        
+
         AuthOutcome outcome = bearer.authenticate(facade);
         if (outcome == AuthOutcome.FAILED) {
             challenge = bearer.getChallenge();
@@ -74,12 +74,28 @@ public abstract class RequestAuthenticator {
             return AuthOutcome.AUTHENTICATED;
         }
 
+        QueryParamterTokenRequestAuthenticator queryParamAuth = createQueryParamterTokenRequestAuthenticator();
+        if (log.isTraceEnabled()) {
+            log.trace("try query paramter auth");
+        }
+
+        outcome = queryParamAuth.authenticate(facade);
+        if (outcome == AuthOutcome.FAILED) {
+            challenge = queryParamAuth.getChallenge();
+            log.debug("QueryParamAuth auth FAILED");
+            return AuthOutcome.FAILED;
+        } else if (outcome == AuthOutcome.AUTHENTICATED) {
+            log.debug("QueryParamAuth AUTHENTICATED");
+            completeAuthentication(queryParamAuth, "KEYCLOAK");
+            return AuthOutcome.AUTHENTICATED;
+        }
+
         if (deployment.isEnableBasicAuth()) {
             BasicAuthRequestAuthenticator basicAuth = createBasicAuthAuthenticator();
             if (log.isTraceEnabled()) {
                 log.trace("try basic auth");
             }
-    
+
             outcome = basicAuth.authenticate(facade);
             if (outcome == AuthOutcome.FAILED) {
                 challenge = basicAuth.getChallenge();
@@ -150,6 +166,10 @@ public abstract class RequestAuthenticator {
         return new BasicAuthRequestAuthenticator(deployment);
     }
 
+    protected QueryParamterTokenRequestAuthenticator createQueryParamterTokenRequestAuthenticator() {
+        return new QueryParamterTokenRequestAuthenticator(deployment);
+    }
+
     protected void completeAuthentication(OAuthRequestAuthenticator oauth) {
         RefreshableKeycloakSecurityContext session = new RefreshableKeycloakSecurityContext(deployment, tokenStore, oauth.getTokenString(), oauth.getToken(), oauth.getIdTokenString(), oauth.getIdToken(), oauth.getRefreshToken());
         final KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal = new KeycloakPrincipal<RefreshableKeycloakSecurityContext>(AdapterUtils.getPrincipalName(deployment, oauth.getToken()), session);
@@ -158,10 +178,12 @@ public abstract class RequestAuthenticator {
     }
 
     protected abstract void completeOAuthAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal);
+
     protected abstract void completeBearerAuthentication(KeycloakPrincipal<RefreshableKeycloakSecurityContext> principal, String method);
 
     /**
      * After code is received, we change the session id if possible to guard against https://www.owasp.org/index.php/Session_Fixation
+     *
      * @param create
      * @return
      */
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
index 15b48fa..a602ffd 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTest.java
@@ -203,4 +203,17 @@ public class AdapterTest {
         testStrategy.testAccountManagementSessionsLogout();
     }
 
+    /**
+     * KEYCLOAK-1733
+     */
+    @Test
+    public void testNullQueryParameterAccessToken() throws Exception {
+        testStrategy.testNullQueryParameterAccessToken();
+    }
+
+    @Test
+    public void testRestCallWithAccessTokenAsQueryParameter() throws Exception {
+        testStrategy.testRestCallWithAccessTokenAsQueryParameter();
+
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
index c5790dc..507c0fe 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/adapter/AdapterTestStrategy.java
@@ -17,34 +17,29 @@
 package org.keycloak.testsuite.adapter;
 
 import org.apache.http.conn.params.ConnManagerParams;
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.junit.Assert;
 import org.junit.rules.ExternalResource;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.adapters.OIDCAuthenticationError;
-import org.keycloak.common.Version;
-import org.keycloak.representations.VersionRepresentation;
 import org.keycloak.admin.client.Keycloak;
+import org.keycloak.common.Version;
+import org.keycloak.common.util.Time;
 import org.keycloak.constants.AdapterConstants;
-import org.keycloak.models.ClientModel;
-import org.keycloak.models.Constants;
-import org.keycloak.models.KeycloakSession;
-import org.keycloak.models.RealmModel;
-import org.keycloak.models.UserModel;
+import org.keycloak.models.*;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
+import org.keycloak.representations.VersionRepresentation;
 import org.keycloak.representations.idm.RealmRepresentation;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.services.managers.RealmManager;
 import org.keycloak.services.managers.ResourceAdminManager;
+import org.keycloak.testsuite.KeycloakServer;
 import org.keycloak.testsuite.OAuthClient;
 import org.keycloak.testsuite.pages.AccountSessionsPage;
 import org.keycloak.testsuite.pages.LoginPage;
-import org.keycloak.testsuite.rule.AbstractKeycloakRule;
-import org.keycloak.testsuite.rule.ErrorServlet;
-import org.keycloak.testsuite.rule.KeycloakRule;
-import org.keycloak.testsuite.rule.WebResource;
-import org.keycloak.testsuite.rule.WebRule;
-import org.keycloak.testsuite.KeycloakServer;
+import org.keycloak.testsuite.rule.*;
 import org.keycloak.util.BasicAuthHelper;
-import org.keycloak.common.util.Time;
 import org.openqa.selenium.WebDriver;
 
 import javax.ws.rs.client.Client;
@@ -59,7 +54,6 @@ import java.net.URI;
 import java.util.List;
 import java.util.Map;
 import java.util.concurrent.atomic.AtomicInteger;
-import org.keycloak.representations.idm.UserRepresentation;
 
 /**
  * Tests Undertow Adapter
@@ -144,7 +138,8 @@ public class AdapterTestStrategy extends ExternalResource {
         System.out.println("insecure: ");
         System.out.println(driver.getPageSource());
         Assert.assertTrue(driver.getPageSource().contains("Insecure Page"));
-        if (System.getProperty("insecure.user.principal.unsupported") == null) Assert.assertTrue(driver.getPageSource().contains("UserPrincipal"));
+        if (System.getProperty("insecure.user.principal.unsupported") == null)
+            Assert.assertTrue(driver.getPageSource().contains("UserPrincipal"));
 
         // test logout
 
@@ -385,6 +380,26 @@ public class AdapterTestStrategy extends ExternalResource {
     }
 
     /**
+     * KEYCLOAK-1733
+     *
+     * @throws Exception
+     */
+    public void testNullQueryParameterAccessToken() throws Exception {
+        Client client = ClientBuilder.newClient();
+        WebTarget target = client.target(APP_SERVER_BASE_URL + "/customer-db/");
+        Response response = target.request().get();
+        Assert.assertEquals(401, response.getStatus());
+        response.close();
+
+        target = client.target(APP_SERVER_BASE_URL + "/customer-db?access_token=");
+        response = target.request().get();
+        Assert.assertEquals(401, response.getStatus());
+        response.close();
+
+        client.close();
+    }
+
+    /**
      * KEYCLOAK-1368
      * @throws Exception
      */
@@ -406,7 +421,7 @@ public class AdapterTestStrategy extends ExternalResource {
         Assert.assertTrue(errorPageResponse.contains("Error Page"));
         response.close();
         Assert.assertNotNull(ErrorServlet.authError);
-        OIDCAuthenticationError error = (OIDCAuthenticationError)ErrorServlet.authError;
+        OIDCAuthenticationError error = (OIDCAuthenticationError) ErrorServlet.authError;
         Assert.assertEquals(OIDCAuthenticationError.Reason.NO_BEARER_TOKEN, error.getReason());
 
         ErrorServlet.authError = null;
@@ -422,7 +437,7 @@ public class AdapterTestStrategy extends ExternalResource {
         Assert.assertTrue(errorPageResponse.contains("Error Page"));
         response.close();
         Assert.assertNotNull(ErrorServlet.authError);
-        error = (OIDCAuthenticationError)ErrorServlet.authError;
+        error = (OIDCAuthenticationError) ErrorServlet.authError;
         Assert.assertEquals(OIDCAuthenticationError.Reason.INVALID_TOKEN, error.getReason());
 
         client.close();
@@ -464,8 +479,8 @@ public class AdapterTestStrategy extends ExternalResource {
         String header = BasicAuthHelper.createHeader("customer-portal", "password");
         Form form = new Form();
         form.param(OAuth2Constants.GRANT_TYPE, OAuth2Constants.PASSWORD)
-            .param("username", "monkey@redhat.com")
-            .param("password", "password");
+                .param("username", "monkey@redhat.com")
+                .param("password", "password");
         Response response = target.request()
                 .header(HttpHeaders.AUTHORIZATION, header)
                 .post(Entity.form(form));
@@ -496,7 +511,6 @@ public class AdapterTestStrategy extends ExternalResource {
     }
 
 
-
     public void testAuthenticated() throws Exception {
         // test login to customer-portal which does a bearer request to customer-db
         driver.navigate().to(APP_SERVER_BASE_URL + "/secure-portal");
@@ -522,6 +536,53 @@ public class AdapterTestStrategy extends ExternalResource {
     }
 
     /**
+     * KEYCLOAK-1733
+     *
+     * @throws Exception
+     */
+    public void testRestCallWithAccessTokenAsQueryParameter() throws Exception {
+        String accessToken = getAccessToken();
+        Client client = ClientBuilder.newClient();
+        try {
+            // test without token
+            Response response = client.target(APP_SERVER_BASE_URL + "/customer-db").request().get();
+            Assert.assertEquals(401, response.getStatus());
+            response.close();
+            // test with access_token as QueryParamter
+            response = client.target(APP_SERVER_BASE_URL + "/customer-db").queryParam("access_token", accessToken).request().get();
+            Assert.assertEquals(200, response.getStatus());
+            response.close();
+        } finally {
+            client.close();
+        }
+    }
+
+    private String getAccessToken() throws JSONException {
+        String tokenUrl = AUTH_SERVER_URL + "/realms/demo/protocol/openid-connect/token";
+
+        Client client = ClientBuilder.newClient();
+        try {
+            WebTarget webTarget = client.target(tokenUrl);
+
+            Form form = new Form();
+            form.param("grant_type", "password");
+            form.param("client_id", "customer-portal-public");
+            form.param("username", "bburke@redhat.com");
+            form.param("password", "password");
+            Response response = webTarget.request().post(Entity.form(form));
+
+            Assert.assertEquals(200, response.getStatus());
+
+            JSONObject jsonObject = new JSONObject(response.readEntity(String.class));
+            System.out.println(jsonObject);
+            response.close();
+            return jsonObject.getString("access_token");
+        } finally {
+            client.close();
+        }
+    }
+
+    /**
      * KEYCLOAK-732
      *
      * @throws Throwable
diff --git a/testsuite/integration/src/test/resources/adapter-test/demorealm.json b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
index 70dc85a..aaac871 100755
--- a/testsuite/integration/src/test/resources/adapter-test/demorealm.json
+++ b/testsuite/integration/src/test/resources/adapter-test/demorealm.json
@@ -157,6 +157,12 @@
             ]
         },
         {
+            "name": "customer-portal-public",
+            "enabled": true,
+            "publicClient": true,
+            "directAccessGrantsEnabled": true
+        },
+        {
             "name": "product-portal",
             "enabled": true,
             "adminUrl": "http://localhost:8081/product-portal",