keycloak-uncached

Details

diff --git a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
index 9d28807..323a6ff 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/endpoints/TokenIntrospectionEndpoint.java
@@ -31,6 +31,7 @@ import org.keycloak.models.ClientModel;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.protocol.oidc.TokenManager;
+import org.keycloak.protocol.oidc.TokenManager.TokenValidation;
 import org.keycloak.protocol.oidc.utils.AuthorizeClientUtil;
 import org.keycloak.representations.AccessToken;
 import org.keycloak.services.ErrorResponseException;
@@ -106,7 +107,8 @@ public class TokenIntrospectionEndpoint {
             AccessToken toIntrospect = toAccessToken(tokenTypeHint, token);
             ObjectNode tokenMetadata;
 
-            if (toIntrospect.isActive()) {
+            boolean active = tokenManager.isTokenValid(session, realm, toIntrospect);
+            if (active) {
                 tokenMetadata = JsonSerialization.createObjectNode(toIntrospect);
                 tokenMetadata.put("client_id", toIntrospect.getIssuedFor());
                 tokenMetadata.put("username", toIntrospect.getPreferredUsername());
@@ -114,7 +116,7 @@ public class TokenIntrospectionEndpoint {
                 tokenMetadata = JsonSerialization.createObjectNode();
             }
 
-            tokenMetadata.put("active", toIntrospect.isActive());
+            tokenMetadata.put("active", active);
 
             this.event.success();
 
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
index e580af6..bbd41c0 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/TokenManager.java
@@ -174,8 +174,31 @@ public class TokenManager {
         verifyAccess(oldToken, newToken);
 
         return new TokenValidation(user, userSession, clientSession, newToken);
+    }
+
+    public boolean isTokenValid(KeycloakSession session, RealmModel realm, AccessToken token) throws OAuthErrorException {
+        if (!token.isActive()) {
+            return false;
+        }
 
+        if (token.getIssuedAt() < realm.getNotBefore()) {
+            return false;
+        }
+
+        UserModel user = session.users().getUserById(token.getSubject(), realm);
+        if (user == null) {
+            return false;
+        }
+        if (!user.isEnabled()) {
+            return false;
+        }
+
+        UserSessionModel userSession =  session.sessions().getUserSession(realm, token.getSessionState());
+        if (!AuthenticationManager.isSessionValid(realm, userSession)) {
+            return false;
+        }
 
+        return true;
     }
 
     public RefreshResult refreshAccessToken(KeycloakSession session, UriInfo uriInfo, ClientConnection connection, RealmModel realm, ClientModel authorizedClient, String encodedRefreshToken, EventBuilder event, HttpHeaders headers) throws OAuthErrorException {
diff --git a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
index 0d7c890..5e37c1e 100755
--- a/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
+++ b/services/src/main/java/org/keycloak/services/resources/admin/UsersResource.java
@@ -166,7 +166,7 @@ public class UsersResource {
                 attrsToRemove = Collections.emptySet();
             }
 
-            if (rep.isEnabled() != null && rep.isEnabled()) {
+            if (rep.isEnabled() != null && rep.isEnabled() && rep.getUsername() != null) {
                 UsernameLoginFailureModel failureModel = session.sessions().getUserLoginFailure(realm, rep.getUsername().toLowerCase());
                 if (failureModel != null) {
                     failureModel.clearFailures();
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
index 95a3c37..88a9247 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/oauth/TokenIntrospectionTest.java
@@ -23,6 +23,7 @@ import org.junit.Rule;
 import org.junit.Test;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.admin.client.Keycloak;
+import org.keycloak.common.util.Time;
 import org.keycloak.events.Event;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.Constants;
@@ -31,6 +32,7 @@ import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.utils.KeycloakModelUtils;
+import org.keycloak.representations.idm.UserRepresentation;
 import org.keycloak.representations.oidc.TokenMetadataRepresentation;
 import org.keycloak.services.managers.ClientManager;
 import org.keycloak.services.managers.RealmManager;
@@ -41,6 +43,7 @@ import org.keycloak.testsuite.pages.LoginPage;
 import org.keycloak.testsuite.rule.KeycloakRule;
 import org.keycloak.testsuite.rule.WebResource;
 import org.keycloak.testsuite.rule.WebRule;
+import org.keycloak.util.JsonSerialization;
 import org.openqa.selenium.WebDriver;
 
 import static org.junit.Assert.*;
@@ -207,4 +210,90 @@ public class TokenIntrospectionTest {
 
         events.clear();
     }
+
+    @Test
+    public void testIntrospectAccessToken() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        Event loginEvent = events.expectLogin().assertEvent();
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertTrue(rep.isActive());
+        assertEquals("test-user@localhost", rep.getUserName());
+        assertEquals("test-app", rep.getClientId());
+        assertEquals(loginEvent.getUserId(), rep.getSubject());
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectAccessTokenSessionInvalid() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+        oauth.doLogout(accessTokenResponse.getRefreshToken(), "password");
+
+        String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+        TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+        assertFalse(rep.isActive());
+        assertNull(rep.getUserName());
+        assertNull(rep.getClientId());
+        assertNull(rep.getSubject());
+
+        events.clear();
+    }
+
+    @Test
+    public void testIntrospectAccessTokenUserDisabled() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        Event loginEvent = events.expectLogin().assertEvent();
+
+        UserRepresentation userRep = new UserRepresentation();
+        try {
+            userRep.setEnabled(false);
+            keycloak.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+
+            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+            assertFalse(rep.isActive());
+            assertNull(rep.getUserName());
+            assertNull(rep.getClientId());
+            assertNull(rep.getSubject());
+
+            events.clear();
+        } finally {
+            userRep.setEnabled(true);
+            keycloak.realm(oauth.getRealm()).users().get(loginEvent.getUserId()).update(userRep);
+        }
+    }
+
+    @Test
+    public void testIntrospectAccessTokenExpired() throws Exception {
+        oauth.doLogin("test-user@localhost", "password");
+        String code = oauth.getCurrentQuery().get(OAuth2Constants.CODE);
+        AccessTokenResponse accessTokenResponse = oauth.doAccessTokenRequest(code, "password");
+
+        try {
+            Time.setOffset(keycloak.realm(oauth.getRealm()).toRepresentation().getAccessTokenLifespan() + 1);
+
+            String tokenResponse = oauth.introspectAccessTokenWithClientCredential("confidential-cli", "secret1", accessTokenResponse.getAccessToken());
+            TokenMetadataRepresentation rep = JsonSerialization.readValue(tokenResponse, TokenMetadataRepresentation.class);
+
+            assertFalse(rep.isActive());
+            assertNull(rep.getUserName());
+            assertNull(rep.getClientId());
+            assertNull(rep.getSubject());
+
+            events.clear();
+        } finally {
+            Time.setOffset(0);
+        }
+    }
 }