keycloak-aplcache

Merge pull request #3748 from mposolda/master KEYCLOAK-4201

1/13/2017 7:20:33 AM

Details

diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
index 8c4235c..e445d57 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/Keycloak.java
@@ -78,8 +78,8 @@ public class Keycloak {
         return new Keycloak(serverUrl, realm, username, password, clientId, null, PASSWORD, null, null);
     }
 
-    public static Keycloak getInstance(String serverUrl, String realm, String clientId, String authtoken) {
-        return new Keycloak(serverUrl, realm, null, null, clientId, null, PASSWORD, null, null);
+    public static Keycloak getInstance(String serverUrl, String realm, String clientId, String authToken) {
+        return new Keycloak(serverUrl, realm, null, null, clientId, null, PASSWORD, null, authToken);
     }
 
     public RealmsResource realms() {
diff --git a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
index 7621eac..bed37e6 100755
--- a/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
+++ b/integration/admin-client/src/main/java/org/keycloak/admin/client/resource/RoleMappingResource.java
@@ -39,7 +39,7 @@ public interface RoleMappingResource {
     @Path("realm")
     public RoleScopeResource realmLevel();
 
-    @Path("clients/{clientId}")
-    public RoleScopeResource clientLevel(@PathParam("clientId") String clientId);
+    @Path("clients/{clientUUID}")
+    public RoleScopeResource clientLevel(@PathParam("clientUUID") String clientUUID);
 
 }
diff --git a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
index d9a57cf..50972f4 100755
--- a/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AppAuthManager.java
@@ -61,7 +61,7 @@ public class AppAuthManager extends AuthenticationManager {
     public AuthResult authenticateBearerToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, HttpHeaders headers) {
         String tokenString = extractAuthorizationHeaderToken(headers);
         if (tokenString == null) return null;
-        AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, tokenString, headers);
+        AuthResult authResult = verifyIdentityToken(session, realm, uriInfo, connection, true, true, false, tokenString, headers);
         return authResult;
     }
 
diff --git a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
index 1392328..5d467da 100755
--- a/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/AuthenticationManager.java
@@ -108,6 +108,15 @@ public class AuthenticationManager {
         return userSession.getLastSessionRefresh() + realm.getSsoSessionIdleTimeout() > currentTime && max > currentTime;
     }
 
+    public static boolean isOfflineSessionValid(RealmModel realm, UserSessionModel userSession) {
+        if (userSession == null) {
+            logger.debug("No offline user session");
+            return false;
+        }
+        int currentTime = Time.currentTime();
+        return userSession.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout() > currentTime;
+    }
+
     public static void expireUserSessionCookie(KeycloakSession session, UserSessionModel userSession, RealmModel realm, UriInfo uriInfo, HttpHeaders headers, ClientConnection connection) {
         try {
             // check to see if any identity cookie is set with the same session and expire it if necessary
@@ -390,7 +399,7 @@ public class AuthenticationManager {
         }
 
         String tokenString = cookie.getValue();
-        AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, tokenString, session.getContext().getRequestHeaders());
+        AuthResult authResult = verifyIdentityToken(session, realm, session.getContext().getUri(), session.getContext().getConnection(), checkActive, false, true, tokenString, session.getContext().getRequestHeaders());
         if (authResult == null) {
             expireIdentityCookie(realm, session.getContext().getUri(), session.getContext().getConnection());
             return null;
@@ -691,7 +700,7 @@ public class AuthenticationManager {
 
 
     protected static AuthResult verifyIdentityToken(KeycloakSession session, RealmModel realm, UriInfo uriInfo, ClientConnection connection, boolean checkActive, boolean checkTokenType,
-                                                    String tokenString, HttpHeaders headers) {
+                                                    boolean isCookie, String tokenString, HttpHeaders headers) {
         try {
             TokenVerifier verifier = TokenVerifier.create(tokenString).realmUrl(Urls.realmIssuer(uriInfo.getBaseUri(), realm.getName())).checkActive(checkActive).checkTokenType(checkTokenType);
             String kid = verifier.getHeader().getKeyId();
@@ -729,6 +738,14 @@ public class AuthenticationManager {
 
             UserSessionModel userSession = session.sessions().getUserSession(realm, token.getSessionState());
             if (!isSessionValid(realm, userSession)) {
+                // Check if accessToken was for the offline session.
+                if (!isCookie) {
+                    UserSessionModel offlineUserSession = session.sessions().getUserSession(realm, token.getSessionState());
+                    if (isOfflineSessionValid(realm, offlineUserSession)) {
+                        return new AuthResult(user, offlineUserSession, token);
+                    }
+                }
+
                 if (userSession != null) backchannelLogout(session, realm, userSession, uriInfo, connection, headers, true);
                 logger.debug("User session not active");
                 return null;
diff --git a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
index 223a3f6..f4cf19c 100755
--- a/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
+++ b/testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/oauth/OfflineTokenTest.java
@@ -23,12 +23,15 @@ import org.junit.Before;
 import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
+import org.keycloak.admin.client.Keycloak;
+import org.keycloak.admin.client.resource.ClientResource;
 import org.keycloak.admin.client.resource.RealmResource;
 import org.keycloak.admin.client.resource.RoleResource;
 import org.keycloak.admin.client.resource.UserResource;
 import org.keycloak.common.constants.ServiceAccountConstants;
 import org.keycloak.events.Details;
 import org.keycloak.events.Errors;
+import org.keycloak.models.AdminRoles;
 import org.keycloak.models.Constants;
 import org.keycloak.models.utils.KeycloakModelUtils;
 import org.keycloak.representations.AccessToken;
@@ -40,6 +43,9 @@ import org.keycloak.representations.idm.RoleRepresentation;
 import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.testsuite.AbstractKeycloakTest;
 import org.keycloak.testsuite.AssertEvents;
+import org.keycloak.testsuite.admin.ApiUtil;
+import org.keycloak.testsuite.arquillian.AuthServerTestEnricher;
+import org.keycloak.testsuite.auth.page.AuthRealm;
 import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.util.ClientBuilder;
 import org.keycloak.testsuite.util.ClientManager;
@@ -432,4 +438,40 @@ public class OfflineTokenTest extends AbstractKeycloakTest {
         testUser.roles().realmLevel().add(Collections.singletonList(offlineAccess));
         
     }
+
+    /**
+     * KEYCLOAK-4201
+     *
+     * @throws Exception
+     */
+    @Test
+    public void offlineTokenAdminRESTAccess() throws Exception {
+        // Grant "view-realm" role to user
+        RealmResource appRealm = adminClient.realm("test");
+        ClientResource realmMgmt = ApiUtil.findClientByClientId(appRealm, Constants.REALM_MANAGEMENT_CLIENT_ID);
+        String realmMgmtUuid = realmMgmt.toRepresentation().getId();
+        RoleRepresentation roleRep = realmMgmt.roles().get(AdminRoles.VIEW_REALM).toRepresentation();
+
+        UserResource testUser = findUserByUsernameId(appRealm, "test-user@localhost");
+        testUser.roles().clientLevel(realmMgmtUuid).add(Collections.singletonList(roleRep));
+
+        // Login with offline token now
+        oauth.scope(OAuth2Constants.OFFLINE_ACCESS);
+        oauth.clientId("offline-client");
+        OAuthClient.AccessTokenResponse tokenResponse = oauth.doGrantAccessTokenRequest("secret1", "test-user@localhost", "password");
+
+        events.clear();
+
+        // Set the time offset, so that "normal" userSession expires
+        setTimeOffset(86400);
+
+        // Refresh with the offline token
+        tokenResponse = oauth.doRefreshTokenRequest(tokenResponse.getRefreshToken(), "secret1");
+
+        // Use accessToken to admin REST request
+        Keycloak offlineTokenAdmin = Keycloak.getInstance(AuthServerTestEnricher.getAuthServerContextRoot() + "/auth",
+                AuthRealm.MASTER, Constants.ADMIN_CLI_CLIENT_ID, tokenResponse.getAccessToken());
+        RealmRepresentation testRealm = offlineTokenAdmin.realm("test").toRepresentation();
+        Assert.assertNotNull(testRealm);
+    }
 }