Merge pull request #3329 from mposolda/master

KEYCLOAK-3626 - CreatedDate and lastUpdatedDate on user consent
This commit is contained in:
Marek Posolda 2016-10-17 15:49:22 +02:00 committed by GitHub
commit dc13c701f5
16 changed files with 134 additions and 2 deletions

View file

@ -35,6 +35,10 @@ public class UserConsentRepresentation {
// Key is clientId, Value is list of granted roles of this client // Key is clientId, Value is list of granted roles of this client
protected Map<String, List<String>> grantedClientRoles; protected Map<String, List<String>> grantedClientRoles;
private Long createdDate;
private Long lastUpdatedDate;
public String getClientId() { public String getClientId() {
return clientId; return clientId;
} }
@ -66,4 +70,20 @@ public class UserConsentRepresentation {
public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) { public void setGrantedClientRoles(Map<String, List<String>> grantedClientRoles) {
this.grantedClientRoles = 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;
}
} }

View file

@ -555,6 +555,8 @@ public class UserCacheSession implements UserCache {
} }
UserConsentModel consentModel = new UserConsentModel(client); UserConsentModel consentModel = new UserConsentModel(client);
consentModel.setCreatedDate(cachedConsent.getCreatedDate());
consentModel.setLastUpdatedDate(cachedConsent.getLastUpdatedDate());
for (String roleId : cachedConsent.getRoleIds()) { for (String roleId : cachedConsent.getRoleIds()) {
RoleModel role = session.realms().getRoleById(roleId, realm); RoleModel role = session.realms().getRoleById(roleId, realm);

View file

@ -32,6 +32,8 @@ public class CachedUserConsent {
private final String clientDbId; private final String clientDbId;
private final Set<ProtocolMapperModel> protocolMappers = new HashSet<>(); private final Set<ProtocolMapperModel> protocolMappers = new HashSet<>();
private final Set<String> roleIds = new HashSet<>(); private final Set<String> roleIds = new HashSet<>();
private final Long createdDate;
private final Long lastUpdatedDate;
public CachedUserConsent(UserConsentModel consentModel) { public CachedUserConsent(UserConsentModel consentModel) {
this.clientDbId = consentModel.getClient().getId(); this.clientDbId = consentModel.getClient().getId();
@ -39,6 +41,8 @@ public class CachedUserConsent {
for (RoleModel role : consentModel.getGrantedRoles()) { for (RoleModel role : consentModel.getGrantedRoles()) {
this.roleIds.add(role.getId()); this.roleIds.add(role.getId());
} }
this.createdDate = consentModel.getCreatedDate();
this.lastUpdatedDate = consentModel.getLastUpdatedDate();
} }
public String getClientDbId() { public String getClientDbId() {
@ -52,4 +56,12 @@ public class CachedUserConsent {
public Set<String> getRoleIds() { public Set<String> getRoleIds() {
return roleIds; return roleIds;
} }
public Long getCreatedDate() {
return createdDate;
}
public Long getLastUpdatedDate() {
return lastUpdatedDate;
}
} }

View file

@ -18,6 +18,7 @@
package org.keycloak.models.jpa; package org.keycloak.models.jpa;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.credential.CredentialModel; import org.keycloak.credential.CredentialModel;
import org.keycloak.credential.UserCredentialStore; 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() + "]"); throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]");
} }
long currentTime = Time.currentTimeMillis();
consentEntity = new UserConsentEntity(); consentEntity = new UserConsentEntity();
consentEntity.setId(KeycloakModelUtils.generateId()); consentEntity.setId(KeycloakModelUtils.generateId());
consentEntity.setUser(em.getReference(UserEntity.class, user.getId())); consentEntity.setUser(em.getReference(UserEntity.class, user.getId()));
consentEntity.setClientId(clientId); consentEntity.setClientId(clientId);
consentEntity.setCreatedDate(currentTime);
consentEntity.setLastUpdatedDate(currentTime);
em.persist(consentEntity); em.persist(consentEntity);
em.flush(); em.flush();
@ -277,6 +282,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
throw new ModelException("Client with id " + entity.getClientId() + " is not available"); throw new ModelException("Client with id " + entity.getClientId() + " is not available");
} }
UserConsentModel model = new UserConsentModel(client); UserConsentModel model = new UserConsentModel(client);
model.setCreatedDate(entity.getCreatedDate());
model.setLastUpdatedDate(entity.getLastUpdatedDate());
Collection<UserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles(); Collection<UserConsentRoleEntity> grantedRoleEntities = entity.getGrantedRoles();
if (grantedRoleEntities != null) { if (grantedRoleEntities != null) {
@ -346,6 +353,8 @@ public class JpaUserProvider implements UserProvider, UserCredentialStore {
em.remove(toRemove); em.remove(toRemove);
} }
consentEntity.setLastUpdatedDate(Time.currentTimeMillis());
em.flush(); em.flush();
} }

View file

@ -68,6 +68,12 @@ public class UserConsentEntity {
@OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent") @OneToMany(cascade ={CascadeType.REMOVE}, orphanRemoval = true, mappedBy = "userConsent")
Collection<UserConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<UserConsentProtocolMapperEntity>(); Collection<UserConsentProtocolMapperEntity> grantedProtocolMappers = new ArrayList<UserConsentProtocolMapperEntity>();
@Column(name = "CREATED_DATE")
private Long createdDate;
@Column(name = "LAST_UPDATED_DATE")
private Long lastUpdatedDate;
public String getId() { public String getId() {
return id; return id;
} }
@ -108,6 +114,22 @@ public class UserConsentEntity {
this.grantedProtocolMappers = 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;
}
@Override @Override
public boolean equals(Object o) { public boolean equals(Object o) {
if (this == o) return true; if (this == o) return true;
@ -125,5 +147,4 @@ public class UserConsentEntity {
public int hashCode() { public int hashCode() {
return id.hashCode(); return id.hashCode();
} }
} }

View file

@ -47,7 +47,11 @@
<dropColumn tableName="REALM" columnName="PUBLIC_KEY" /> <dropColumn tableName="REALM" columnName="PUBLIC_KEY" />
<dropColumn tableName="REALM" columnName="CERTIFICATE" /> <dropColumn tableName="REALM" columnName="CERTIFICATE" />
<addColumn tableName="USER_CONSENT">
<column name="CREATED_DATE" type="BIGINT"/>
<column name="LAST_UPDATED_DATE" type="BIGINT"/>
</addColumn>
</changeSet> </changeSet>
</databaseChangeLog> </databaseChangeLog>

View file

@ -21,6 +21,7 @@ import com.mongodb.BasicDBObject;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import com.mongodb.QueryBuilder; import com.mongodb.QueryBuilder;
import org.keycloak.common.util.MultivaluedHashMap; import org.keycloak.common.util.MultivaluedHashMap;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel; import org.keycloak.component.ComponentModel;
import org.keycloak.connections.mongo.api.MongoStore; import org.keycloak.connections.mongo.api.MongoStore;
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext; 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() + "]"); throw new ModelDuplicateException("Consent already exists for client [" + clientId + "] and user [" + user.getId() + "]");
} }
long currentTime = Time.currentTimeMillis();
MongoUserConsentEntity consentEntity = new MongoUserConsentEntity(); MongoUserConsentEntity consentEntity = new MongoUserConsentEntity();
consentEntity.setUserId(user.getId()); consentEntity.setUserId(user.getId());
consentEntity.setClientId(clientId); consentEntity.setClientId(clientId);
consentEntity.setCreatedDate(currentTime);
consentEntity.setLastUpdatedDate(currentTime);
fillEntityFromModel(consent, consentEntity); fillEntityFromModel(consent, consentEntity);
getMongoStore().insertEntity(consentEntity, invocationContext); 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"); throw new ModelException("Client with id " + entity.getClientId() + " is not available");
} }
UserConsentModel model = new UserConsentModel(client); UserConsentModel model = new UserConsentModel(client);
model.setCreatedDate(entity.getCreatedDate());
model.setLastUpdatedDate(entity.getLastUpdatedDate());
for (String roleId : entity.getGrantedRoles()) { for (String roleId : entity.getGrantedRoles()) {
RoleModel roleModel = realm.getRoleById(roleId); RoleModel roleModel = realm.getRoleById(roleId);
@ -596,6 +603,7 @@ public class MongoUserProvider implements UserProvider, UserCredentialStore {
protMapperIds.add(protMapperModel.getId()); protMapperIds.add(protMapperModel.getId());
} }
consentEntity.setGrantedProtocolMappers(protMapperIds); consentEntity.setGrantedProtocolMappers(protMapperIds);
consentEntity.setLastUpdatedDate(Time.currentTimeMillis());
} }
@Override @Override

View file

@ -29,6 +29,8 @@ public class UserConsentEntity extends AbstractIdentifiableEntity {
private String clientId; private String clientId;
private List<String> grantedRoles = new ArrayList<String>(); private List<String> grantedRoles = new ArrayList<String>();
private List<String> grantedProtocolMappers = new ArrayList<String>(); private List<String> grantedProtocolMappers = new ArrayList<String>();
private Long createdDate;
private Long lastUpdatedDate;
public String getUserId() { public String getUserId() {
return userId; return userId;
@ -61,4 +63,20 @@ public class UserConsentEntity extends AbstractIdentifiableEntity {
public void setGrantedProtocolMappers(List<String> grantedProtocolMappers) { public void setGrantedProtocolMappers(List<String> grantedProtocolMappers) {
this.grantedProtocolMappers = 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;
}
} }

View file

@ -28,6 +28,8 @@ public class UserConsentModel {
private final ClientModel client; private final ClientModel client;
private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>(); private Set<ProtocolMapperModel> protocolMappers = new HashSet<ProtocolMapperModel>();
private Set<RoleModel> roles = new HashSet<RoleModel>(); private Set<RoleModel> roles = new HashSet<RoleModel>();
private Long createdDate;
private Long lastUpdatedDate;
public UserConsentModel(ClientModel client) { public UserConsentModel(ClientModel client) {
this.client = client; this.client = client;
@ -67,4 +69,19 @@ public class UserConsentModel {
return false; 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;
}
} }

View file

@ -710,6 +710,8 @@ public class ModelToRepresentation {
consentRep.setGrantedProtocolMappers(grantedProtocolMappers); consentRep.setGrantedProtocolMappers(grantedProtocolMappers);
consentRep.setGrantedRealmRoles(grantedRealmRoles); consentRep.setGrantedRealmRoles(grantedRealmRoles);
consentRep.setGrantedClientRoles(grantedClientRoles); consentRep.setGrantedClientRoles(grantedClientRoles);
consentRep.setCreatedDate(model.getCreatedDate());
consentRep.setLastUpdatedDate(model.getLastUpdatedDate());
return consentRep; return consentRep;
} }

View file

@ -1571,6 +1571,8 @@ public class RepresentationToModel {
} }
UserConsentModel consentModel = new UserConsentModel(client); UserConsentModel consentModel = new UserConsentModel(client);
consentModel.setCreatedDate(consentRep.getCreatedDate());
consentModel.setLastUpdatedDate(consentRep.getLastUpdatedDate());
if (consentRep.getGrantedRealmRoles() != null) { if (consentRep.getGrantedRealmRoles() != null) {
for (String roleName : consentRep.getGrantedRealmRoles()) { for (String roleName : consentRep.getGrantedRealmRoles()) {

View file

@ -538,6 +538,8 @@ public class UsersResource {
currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers())); currentRep.put("grantedProtocolMappers", (rep==null ? Collections.emptyMap() : rep.getGrantedProtocolMappers()));
currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles())); currentRep.put("grantedRealmRoles", (rep==null ? Collections.emptyList() : rep.getGrantedRealmRoles()));
currentRep.put("grantedClientRoles", (rep==null ? Collections.emptyMap() : rep.getGrantedClientRoles())); 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<>(); List<Map<String, String>> additionalGrants = new LinkedList<>();
if (hasOfflineToken) { if (hasOfflineToken) {

View file

@ -105,12 +105,16 @@ public class UserConsentModelTest extends AbstractModelTest {
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent)); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnFooConsent));
Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent)); Assert.assertTrue(isRoleGranted(barClient, "bar-client-role", johnFooConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", 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()); UserConsentModel johnBarConsent = realmManager.getSession().users().getConsentByClient(realm, john, barClient.getId());
Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1); Assert.assertEquals(johnBarConsent.getGrantedRoles().size(), 1);
Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1); Assert.assertEquals(johnBarConsent.getGrantedProtocolMappers().size(), 1);
Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent)); Assert.assertTrue(isRoleGranted(realm, "realm-role", johnBarConsent));
Assert.assertTrue(isMapperGranted(barClient, "bar", 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()); UserConsentModel maryConsent = realmManager.getSession().users().getConsentByClient(realm, mary, fooClient.getId());
Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1); Assert.assertEquals(maryConsent.getGrantedRoles().size(), 1);
@ -118,6 +122,8 @@ public class UserConsentModelTest extends AbstractModelTest {
Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent)); Assert.assertTrue(isRoleGranted(realm, "realm-role", maryConsent));
Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent)); Assert.assertFalse(isRoleGranted(barClient, "bar-client-role", maryConsent));
Assert.assertTrue(isMapperGranted(fooClient, "foo", 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())); 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.assertFalse(isRoleGranted(realm, "realm-role", johnConsent));
Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent)); Assert.assertTrue(isRoleGranted(realm, "new-realm-role", johnConsent));
Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent)); Assert.assertFalse(isMapperGranted(fooClient, "foo", johnConsent));
Assert.assertTrue("Created date should be less than last updated date", johnConsent.getCreatedDate() < johnConsent.getLastUpdatedDate());
} }
@Test @Test

View file

@ -896,6 +896,8 @@ spi=SPI
granted-roles=Granted Roles granted-roles=Granted Roles
granted-protocol-mappers=Granted Protocol Mappers granted-protocol-mappers=Granted Protocol Mappers
additional-grants=Additional Grants additional-grants=Additional Grants
consent-created-date=Created
consent-last-updated-date=Last updated
revoke=Revoke revoke=Revoke
new-password=New Password new-password=New Password
password-confirmation=Password Confirmation password-confirmation=Password Confirmation

View file

@ -863,6 +863,8 @@ spi=SPI
granted-roles=Tildelte roller granted-roles=Tildelte roller
granted-protocol-mappers=Innvilgede protokollmappere granted-protocol-mappers=Innvilgede protokollmappere
additional-grants=Tillegsrettigheter additional-grants=Tillegsrettigheter
consent-created-date=Opprettet
consent-last-updated-date=Sist oppdatert
revoke=Opphev revoke=Opphev
new-password=Nytt passord new-password=Nytt passord
password-confirmation=Passord bekreftelse password-confirmation=Passord bekreftelse

View file

@ -13,6 +13,8 @@
<th>{{:: 'granted-roles' | translate}}</th> <th>{{:: 'granted-roles' | translate}}</th>
<th>{{:: 'granted-protocol-mappers' | translate}}</th> <th>{{:: 'granted-protocol-mappers' | translate}}</th>
<th>{{:: 'additional-grants' | translate}}</th> <th>{{:: 'additional-grants' | translate}}</th>
<th>{{:: 'consent-created-date' | translate}}</th>
<th>{{:: 'consent-last-updated-date' | translate}}</th>
<th>{{:: 'action' | translate}}</th> <th>{{:: 'action' | translate}}</th>
</tr> </tr>
</thead> </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 ng-if="!$first">, </span><a href="#/realms/{{realm.realm}}/users/{{user.id}}/offline-sessions/{{additionalGrant.client}}">{{additionalGrant.key}}</a>
</span> </span>
</td> </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> <td class="kc-action-cell" ng-click="revokeConsent(consent.clientId)">{{:: 'revoke' | translate}}</td>
</tr> </tr>
</tbody> </tbody>