keycloak-aplcache

Merge pull request #2400 from mposolda/1.9.x Caching of

3/22/2016 4:03:36 AM

Details

diff --git a/common/src/main/java/org/keycloak/common/util/Base64.java b/common/src/main/java/org/keycloak/common/util/Base64.java
index 163bf2e..b90f1b2 100644
--- a/common/src/main/java/org/keycloak/common/util/Base64.java
+++ b/common/src/main/java/org/keycloak/common/util/Base64.java
@@ -561,7 +561,7 @@ public class Base64
         while( raw.hasRemaining() ){
             int rem = Math.min(3,raw.remaining());
             raw.get(raw3,0,rem);
-            Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS );
+            Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS);
             encoded.put(enc4);
         }   // end input remaining
     }
@@ -1282,7 +1282,12 @@ public class Base64
 
                 }   // end try
                 catch( java.io.IOException e ) {
-                    e.printStackTrace();
+                    if (e.getMessage().equals("Unsupported compression method")) {
+                        System.out.println("Base64 decoding: Ignoring GZIP header and just returning originally-decoded bytes."); // Better to log as debug, but jboss logging not available in the module :/
+                    } else {
+                        e.printStackTrace();
+                    }
+
                     // Just return originally-decoded bytes
                 }   // end catch
                 finally {
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
index ccd6668..b5d962c 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUser.java
@@ -21,12 +21,15 @@ import org.keycloak.common.util.MultivaluedHashMap;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
 
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.LinkedList;
 import java.util.List;
+import java.util.Map;
 import java.util.Set;
 
 /**
@@ -50,6 +53,7 @@ public class CachedUser extends AbstractRevisioned implements InRealm {
     private Set<String> requiredActions = new HashSet<>();
     private Set<String> roleMappings = new HashSet<>();
     private Set<String> groups = new HashSet<>();
+    private Map<String, CachedUserConsent> consents = new HashMap<>(); // Key is client DB Id
 
 
 
@@ -78,6 +82,13 @@ public class CachedUser extends AbstractRevisioned implements InRealm {
                 groups.add(group.getId());
             }
         }
+
+        List<UserConsentModel> consents = user.getConsents();
+        if (consents != null) {
+            for (UserConsentModel consent : consents) {
+                this.consents.put(consent.getClient().getId(), new CachedUserConsent(consent));
+            }
+        }
     }
 
     public String getRealm() {
@@ -143,4 +154,8 @@ public class CachedUser extends AbstractRevisioned implements InRealm {
     public Set<String> getGroups() {
         return groups;
     }
+
+    public Map<String, CachedUserConsent> getConsents() {
+        return consents;
+    }
 }
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java
new file mode 100644
index 0000000..00022ac
--- /dev/null
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/entities/CachedUserConsent.java
@@ -0,0 +1,55 @@
+/*
+ * 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.models.cache.infinispan.entities;
+
+import java.util.HashSet;
+import java.util.Set;
+
+import org.keycloak.models.ProtocolMapperModel;
+import org.keycloak.models.RoleModel;
+import org.keycloak.models.UserConsentModel;
+
+/**
+ * @author <a href="mailto:mposolda@redhat.com">Marek Posolda</a>
+ */
+public class CachedUserConsent {
+
+    private final String clientDbId;
+    private final Set<ProtocolMapperModel> protocolMappers = new HashSet<>();
+    private final Set<String> roleIds = new HashSet<>();
+
+    public CachedUserConsent(UserConsentModel consentModel) {
+        this.clientDbId = consentModel.getClient().getId();
+        this.protocolMappers.addAll(consentModel.getGrantedProtocolMappers());
+        for (RoleModel role : consentModel.getGrantedRoles()) {
+            this.roleIds.add(role.getId());
+        }
+    }
+
+    public String getClientDbId() {
+        return clientDbId;
+    }
+
+    public Set<ProtocolMapperModel> getProtocolMappers() {
+        return protocolMappers;
+    }
+
+    public Set<String> getRoleIds() {
+        return roleIds;
+    }
+}
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
index f628bb5..2b02cee 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserAdapter.java
@@ -20,6 +20,7 @@ package org.keycloak.models.cache.infinispan;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.GroupModel;
 import org.keycloak.models.KeycloakSession;
+import org.keycloak.models.ProtocolMapperModel;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleContainerModel;
 import org.keycloak.models.RoleModel;
@@ -28,10 +29,13 @@ import org.keycloak.models.UserCredentialModel;
 import org.keycloak.models.UserCredentialValueModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.models.cache.infinispan.entities.CachedUser;
+import org.keycloak.models.cache.infinispan.entities.CachedUserConsent;
 import org.keycloak.models.utils.KeycloakModelUtils;
 
+import java.util.Collection;
 import java.util.Collections;
 import java.util.HashSet;
+import java.util.LinkedList;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
@@ -392,16 +396,28 @@ public class UserAdapter implements UserModel {
 
     @Override
     public UserConsentModel getConsentByClient(String clientId) {
-        // TODO: caching?
-        getDelegateForUpdate();
-        return updated.getConsentByClient(clientId);
+        if (updated != null) return updated.getConsentByClient(clientId);
+        CachedUserConsent cachedConsent = cached.getConsents().get(clientId);
+        if (cachedConsent == null) {
+            return null;
+        }
+
+        return toConsentModel(cachedConsent);
     }
 
     @Override
     public List<UserConsentModel> getConsents() {
-        // TODO: caching?
-        getDelegateForUpdate();
-        return updated.getConsents();
+        if (updated != null) return updated.getConsents();
+        Collection<CachedUserConsent> cachedConsents = cached.getConsents().values();
+
+        List<UserConsentModel> result = new LinkedList<>();
+        for (CachedUserConsent cachedConsent : cachedConsents) {
+            UserConsentModel consent = toConsentModel(cachedConsent);
+            if (consent != null) {
+                result.add(consent);
+            }
+        }
+        return result;
     }
 
     @Override
@@ -416,6 +432,26 @@ public class UserAdapter implements UserModel {
         return updated.revokeConsentForClient(clientId);
     }
 
+    private UserConsentModel toConsentModel(CachedUserConsent cachedConsent) {
+        ClientModel client = keycloakSession.realms().getClientById(cachedConsent.getClientDbId(), realm);
+        if (client == null) {
+            return null;
+        }
+
+        UserConsentModel consentModel = new UserConsentModel(client);
+
+        for (String roleId : cachedConsent.getRoleIds()) {
+            RoleModel role = keycloakSession.realms().getRoleById(roleId, realm);
+            if (role != null) {
+                consentModel.addGrantedRole(role);
+            }
+        }
+        for (ProtocolMapperModel protocolMapper : cachedConsent.getProtocolMappers()) {
+            consentModel.addGrantedProtocolMapper(protocolMapper);
+        }
+        return consentModel;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
diff --git a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
index bd9b968..64f4ad5 100755
--- a/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
+++ b/model/infinispan/src/main/java/org/keycloak/models/cache/infinispan/UserCacheSession.java
@@ -18,6 +18,7 @@
 package org.keycloak.models.cache.infinispan;
 
 import org.jboss.logging.Logger;
+import org.keycloak.common.constants.ServiceAccountConstants;
 import org.keycloak.models.ClientModel;
 import org.keycloak.models.CredentialValidationOutput;
 import org.keycloak.models.FederatedIdentityModel;
@@ -297,6 +298,13 @@ public class UserCacheSession implements CacheUserProvider {
 
     @Override
     public UserModel getUserByServiceAccountClient(ClientModel client) {
+        // Just an attempt to find the user from cache by default serviceAccount username
+        String username = ServiceAccountConstants.SERVICE_ACCOUNT_USER_PREFIX + client.getClientId();
+        UserModel user = getUserByUsername(username, client.getRealm());
+        if (user != null && user.getServiceAccountClientLink() != null && user.getServiceAccountClientLink().equals(client.getId())) {
+            return user;
+        }
+
         return getDelegate().getUserByServiceAccountClient(client);
     }
 
diff --git a/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java b/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java
index 329b729..391d9a6 100755
--- a/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java
+++ b/services/src/main/java/org/keycloak/exportimport/dir/DirImportProvider.java
@@ -133,12 +133,13 @@ public class DirImportProvider implements ImportProvider {
 
         if (realmImported.get()) {
             // Import users
-            for (File userFile : userFiles) {
+            for (final File userFile : userFiles) {
                 final FileInputStream fis = new FileInputStream(userFile);
                 KeycloakModelUtils.runJobInTransaction(factory, new ExportImportSessionTask() {
                     @Override
                     protected void runExportImportTask(KeycloakSession session) throws IOException {
                         ImportUtils.importUsersFromStream(session, realmName, JsonSerialization.mapper, fis);
+                        logger.infof("Imported users from %s", userFile.getAbsolutePath());
                     }
                 });
             }