keycloak-uncached

Details

diff --git a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
index a394b3c..681a03b 100644
--- a/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
+++ b/core/src/main/java/org/keycloak/representations/idm/UserConsentRepresentation.java
@@ -35,6 +35,10 @@ public class UserConsentRepresentation {
     // Key is clientId, Value is list of granted roles of this client
     protected Map<String, List<String>> grantedClientRoles;
 
+    private Long createdDate;
+
+    private Long lastUpdatedDate;
+
     public String getClientId() {
         return clientId;
     }
@@ -66,4 +70,20 @@ public class UserConsentRepresentation {
     public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
         this.grantedClientRoles = grantedClientRoles;
     }
+
+    public void setCreatedDate(Long createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public Long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setLastUpdatedDate(Long lastUpdatedDate) {
+        this.lastUpdatedDate = lastUpdatedDate;
+    }
+
+    public Long getLastUpdatedDate() {
+        return lastUpdatedDate;
+    }
 }
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
index e57d456..4b24bd1 100644
--- 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
@@ -32,6 +32,8 @@ public class CachedUserConsent {
     private final String clientDbId;
     private final Set<ProtocolMapperModel> protocolMappers = new HashSet<>();
     private final Set<String> roleIds = new HashSet<>();
+    private final Long createdDate;
+    private final Long lastUpdatedDate;
 
     public CachedUserConsent(UserConsentModel consentModel) {
         this.clientDbId = consentModel.getClient().getId();
@@ -39,6 +41,8 @@ public class CachedUserConsent {
         for (RoleModel role : consentModel.getGrantedRoles()) {
             this.roleIds.add(role.getId());
         }
+        this.createdDate = consentModel.getCreatedDate();
+        this.lastUpdatedDate = consentModel.getLastUpdatedDate();
     }
 
     public String getClientDbId() {
@@ -52,4 +56,12 @@ public class CachedUserConsent {
     public Set<String> getRoleIds() {
         return roleIds;
     }
+
+    public Long getCreatedDate() {
+        return createdDate;
+    }
+
+    public Long getLastUpdatedDate() {
+        return lastUpdatedDate;
+    }
 }
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 16987d4..cdb79a7 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
@@ -555,6 +555,8 @@ public class UserCacheSession implements UserCache {
         }
 
         UserConsentModel consentModel = new UserConsentModel(client);
+        consentModel.setCreatedDate(cachedConsent.getCreatedDate());
+        consentModel.setLastUpdatedDate(cachedConsent.getLastUpdatedDate());
 
         for (String roleId : cachedConsent.getRoleIds()) {
             RoleModel role = session.realms().getRoleById(roleId, realm);
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
index 34c272a..c2b7b02 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/entities/UserConsentEntity.java
@@ -68,6 +68,12 @@ public class UserConsentEntity {
     @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
     Collection<UserConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<UserConsentProtocolMapperEntity>();
 
+    @Column(name = "CREATED_DATE")
+    private Long createdDate;
+
+    @Column(name = "LAST_UPDATED_DATE")
+    private Long lastUpdatedDate;
+
     public String getId() {
         return id;
     }
@@ -108,6 +114,22 @@ public class UserConsentEntity {
         this.grantedProtocolMappers = grantedProtocolMappers;
     }
 
+    public Long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(Long createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public Long getLastUpdatedDate() {
+        return lastUpdatedDate;
+    }
+
+    public void setLastUpdatedDate(Long lastUpdatedDate) {
+        this.lastUpdatedDate = lastUpdatedDate;
+    }
+
     @Override
     public boolean equals(Object o) {
         if (this == o) return true;
@@ -125,5 +147,4 @@ public class UserConsentEntity {
     public int hashCode() {
         return id.hashCode();
     }
-
 }
diff --git a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
index 6f85e38..3aa71f0 100755
--- a/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
+++ b/model/jpa/src/main/java/org/keycloak/models/jpa/JpaUserProvider.java
@@ -18,6 +18,7 @@
 package org.keycloak.models.jpa;
 
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.common.util.Time;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.credential.CredentialModel;
 import org.keycloak.credential.UserCredentialStore;
@@ -201,10 +202,14 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
             throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]");
         }
 
+        long currentTime = Time.currentTimeMillis();
+
         consentEntity = new UserConsentEntity();
         consentEntity.setId(KeycloakModelUtils.generateId());
         consentEntity.setUser(em.getReference(UserEntity.class, user.getId()));
         consentEntity.setClientId(clientId);
+        consentEntity.setCreatedDate(currentTime);
+        consentEntity.setLastUpdatedDate(currentTime);
         em.persist(consentEntity);
         em.flush();
 
@@ -277,6 +282,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
             throw new ModelException("Client with id " + entity.getClientId() + " is not available");
         }
         UserConsentModel model = new UserConsentModel(client);
+        model.setCreatedDate(entity.getCreatedDate());
+        model.setLastUpdatedDate(entity.getLastUpdatedDate());
 
         Collection<UserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
         if (grantedRoleEntities != null) {
@@ -346,6 +353,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
             em.remove(toRemove);
         }
 
+        consentEntity.setLastUpdatedDate(Time.currentTimeMillis());
+
         em.flush();
     }
 
diff --git a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml
index bc2ef13..2f377c3 100755
--- a/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml
+++ b/model/jpa/src/main/resources/META-INF/jpa-changelog-2.3.0.xml
@@ -47,7 +47,11 @@
          <dropColumn tableName="REALM" columnName="PUBLIC_KEY" />
          <dropColumn tableName="REALM" columnName="CERTIFICATE" />
 
-     </changeSet>
+         <addColumn tableName="USER_CONSENT">
+             <column name="CREATED_DATE" type="BIGINT"/>
+             <column name="LAST_UPDATED_DATE" type="BIGINT"/>
+         </addColumn>
 
+     </changeSet>
 
 </databaseChangeLog>
\ No newline at end of file
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
index 529aabe..af1b6be 100755
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/adapters/MongoUserProvider.java
@@ -21,6 +21,7 @@ import com.mongodb.BasicDBObject;
 import com.mongodb.DBObject;
 import com.mongodb.QueryBuilder;
 import org.keycloak.common.util.MultivaluedHashMap;
+import org.keycloak.common.util.Time;
 import org.keycloak.component.ComponentModel;
 import org.keycloak.connections.mongo.api.MongoStore;
 import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
@@ -524,9 +525,13 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
             throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]");
         }
 
+        long currentTime = Time.currentTimeMillis();
+
         MongoUserConsentEntity consentEntity = new MongoUserConsentEntity();
         consentEntity.setUserId(user.getId());
         consentEntity.setClientId(clientId);
+        consentEntity.setCreatedDate(currentTime);
+        consentEntity.setLastUpdatedDate(currentTime);
         fillEntityFromModel(consent, consentEntity);
         getMongoStore().insertEntity(consentEntity, invocationContext);
     }
@@ -568,6 +573,8 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
             throw new ModelException("Client with id " + entity.getClientId() + " is not available");
         }
         UserConsentModel model = new UserConsentModel(client);
+        model.setCreatedDate(entity.getCreatedDate());
+        model.setLastUpdatedDate(entity.getLastUpdatedDate());
 
         for (String roleId : entity.getGrantedRoles()) {
             RoleModel roleModel = realm.getRoleById(roleId);
@@ -596,6 +603,7 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
             protMapperIds.add(protMapperModel.getId());
         }
         consentEntity.setGrantedProtocolMappers(protMapperIds);
+        consentEntity.setLastUpdatedDate(Time.currentTimeMillis());
     }
 
     @Override
diff --git a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java
index c60faee..1ca9e64 100644
--- a/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java
+++ b/model/mongo/src/main/java/org/keycloak/models/mongo/keycloak/entities/UserConsentEntity.java
@@ -29,6 +29,8 @@ public class UserConsentEntity extends AbstractIdentifiableEntity {
     private String clientId;
     private List<String> grantedRoles = new ArrayList<String>();
     private List<String> grantedProtocolMappers = new ArrayList<String>();
+    private Long createdDate;
+    private Long lastUpdatedDate;
 
     public String getUserId() {
         return userId;
@@ -61,4 +63,20 @@ public class UserConsentEntity extends AbstractIdentifiableEntity {
     public void setGrantedProtocolMappers(List<String> grantedProtocolMappers) {
         this.grantedProtocolMappers = grantedProtocolMappers;
     }
+
+    public Long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(Long createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public Long getLastUpdatedDate() {
+        return lastUpdatedDate;
+    }
+
+    public void setLastUpdatedDate(Long lastUpdatedDate) {
+        this.lastUpdatedDate = lastUpdatedDate;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
index d3c295b..847696d 100644
--- a/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/UserConsentModel.java
@@ -28,6 +28,8 @@ public class UserConsentModel {
     private final ClientModel client;
     private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
     private Set<RoleModel> roles = new HashSet<RoleModel>();
+    private Long createdDate;
+    private Long lastUpdatedDate;
 
     public UserConsentModel(ClientModel client) {
         this.client = client;
@@ -67,4 +69,19 @@ public class UserConsentModel {
         return false;
     }
 
+    public Long getCreatedDate() {
+        return createdDate;
+    }
+
+    public void setCreatedDate(Long createdDate) {
+        this.createdDate = createdDate;
+    }
+
+    public Long getLastUpdatedDate() {
+        return lastUpdatedDate;
+    }
+
+    public void setLastUpdatedDate(Long lastUpdatedDate) {
+        this.lastUpdatedDate = lastUpdatedDate;
+    }
 }
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
index 8703a05..9846d08 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/ModelToRepresentation.java
@@ -710,6 +710,8 @@ public class ModelToRepresentation {
         consentRep.setGrantedProtocolMappers(grantedProtocolMappers);
         consentRep.setGrantedRealmRoles(grantedRealmRoles);
         consentRep.setGrantedClientRoles(grantedClientRoles);
+        consentRep.setCreatedDate(model.getCreatedDate());
+        consentRep.setLastUpdatedDate(model.getLastUpdatedDate());
         return consentRep;
     }
 
diff --git a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
index ec516b6..c5e55a5 100755
--- a/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
+++ b/server-spi/src/main/java/org/keycloak/models/utils/RepresentationToModel.java
@@ -1571,6 +1571,8 @@ public class RepresentationToModel {
         }
 
         UserConsentModel consentModel = new UserConsentModel(client);
+        consentModel.setCreatedDate(consentRep.getCreatedDate());
+        consentModel.setLastUpdatedDate(consentRep.getLastUpdatedDate());
 
         if (consentRep.getGrantedRealmRoles() != null) {
             for (String roleName : consentRep.getGrantedRealmRoles()) {
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 2b06c4c..5325d8b 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
@@ -538,6 +538,8 @@ public class UsersResource {
             currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
             currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
             currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles()));
+            currentRep.put("createdDate", (rep==null ? null : rep.getCreatedDate()));
+            currentRep.put("lastUpdatedDate", (rep==null ? null : rep.getLastUpdatedDate()));
 
             List<Map<String, String>> additionalGrants = new LinkedList<>();
             if (hasOfflineToken) {
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
index 4335c52..c65fee1 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/model/UserConsentModelTest.java
@@ -105,12 +105,16 @@ public class UserConsentModelTest extends AbstractModelTest {
         Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
         Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
         Assert.assertTrue(isMapperGranted(fooClient, "foo", johnFooConsent));
+        Assert.assertNotNull("Created Date should be set", johnFooConsent.getCreatedDate());
+        Assert.assertNotNull("Last Updated Date should be set", johnFooConsent.getLastUpdatedDate());
 
         UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john, barClient.getId());
         Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
         Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
         Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
         Assert.assertTrue(isMapperGranted(barClient, "bar", johnBarConsent));
+        Assert.assertNotNull("Created Date should be set", johnBarConsent.getCreatedDate());
+        Assert.assertNotNull("Last Updated Date should be set", johnBarConsent.getLastUpdatedDate());
 
         UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary, fooClient.getId());
         Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
@@ -118,6 +122,8 @@ public class UserConsentModelTest extends AbstractModelTest {
         Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
         Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
         Assert.assertTrue(isMapperGranted(fooClient, "foo", maryConsent));
+        Assert.assertNotNull("Created Date should be set", maryConsent.getCreatedDate());
+        Assert.assertNotNull("Last Updated Date should be set", maryConsent.getLastUpdatedDate());
 
         Assert.assertNull(realmManager.getSession().users().getConsentByClient(realm, mary, barClient.getId()));
     }
@@ -176,6 +182,7 @@ public class UserConsentModelTest extends AbstractModelTest {
         Assert.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
         Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
         Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
+        Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
     }
 
     @Test
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
index ef27439..ef8ef74 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties
@@ -896,6 +896,8 @@ spi=SPI
 granted-roles=Granted Roles
 granted-protocol-mappers=Granted Protocol Mappers
 additional-grants=Additional Grants
+consent-created-date=Created
+consent-last-updated-date=Last updated
 revoke=Revoke
 new-password=New Password
 password-confirmation=Password Confirmation
diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties
index 574eb91..9b8a6cb 100644
--- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties
+++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_no.properties
@@ -863,6 +863,8 @@ spi=SPI
 granted-roles=Tildelte roller
 granted-protocol-mappers=Innvilgede protokollmappere
 additional-grants=Tillegsrettigheter
+consent-created-date=Opprettet
+consent-last-updated-date=Sist oppdatert
 revoke=Opphev
 new-password=Nytt passord
 password-confirmation=Passord bekreftelse
diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
index 63bfa7e..670d708 100644
--- a/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
+++ b/themes/src/main/resources/theme/base/admin/resources/partials/user-consents.html
@@ -13,6 +13,8 @@
             <th>{{:: 'granted-roles' | translate}}</th>
             <th>{{:: 'granted-protocol-mappers' | translate}}</th>
             <th>{{:: 'additional-grants' | translate}}</th>
+            <th>{{:: 'consent-created-date' | translate}}</th>
+            <th>{{:: 'consent-last-updated-date' | translate}}</th>
             <th>{{:: 'action' | translate}}</th>
         </tr>
         </thead>
@@ -41,6 +43,8 @@
                     <span ng-if="!$first">, </span><a href="#/realms/{{realm.realm}}/users/{{user.id}}/offline-sessions/{{additionalGrant.client}}">{{additionalGrant.key}}</a>
                 </span>
             </td>
+            <td>{{consent.createdDate | date :'short'}}</td>
+            <td>{{consent.lastUpdatedDate | date :'short'}}</td>
             <td class="kc-action-cell" ng-click="revokeConsent(consent.clientId)">{{:: 'revoke' | translate}}</td>
         </tr>
         </tbody>