KEYCLOAK-1421 Added timestamp of keycloak user creation into user models
and persistence
This commit is contained in:
parent
aaa83dc19a
commit
d7379f29da
21 changed files with 206 additions and 37 deletions
|
@ -106,5 +106,8 @@
|
||||||
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REQUIRED_ACTION_PROVIDER" constraintName="FK_REQ_ACT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
<addForeignKeyConstraint baseColumnNames="REALM_ID" baseTableName="REQUIRED_ACTION_PROVIDER" constraintName="FK_REQ_ACT_REALM" referencedColumnNames="ID" referencedTableName="REALM"/>
|
||||||
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
|
<addForeignKeyConstraint baseColumnNames="CLIENT_SESSION" baseTableName="CLIENT_USER_SESSION_NOTE" constraintName="FK_CL_USR_SES_NOTE" referencedColumnNames="ID" referencedTableName="CLIENT_SESSION"/>
|
||||||
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
|
<dropColumn tableName="CLIENT_SESSION" columnName="ACTION"/>
|
||||||
|
<addColumn tableName="USER_ENTITY">
|
||||||
|
<column name="CREATED_TIMESTAMP" type="BIGINT"/>
|
||||||
|
</addColumn>
|
||||||
</changeSet>
|
</changeSet>
|
||||||
</databaseChangeLog>
|
</databaseChangeLog>
|
||||||
|
|
|
@ -13,6 +13,7 @@ public class UserRepresentation {
|
||||||
|
|
||||||
protected String self; // link
|
protected String self; // link
|
||||||
protected String id;
|
protected String id;
|
||||||
|
protected Long createdTimestamp;
|
||||||
protected String username;
|
protected String username;
|
||||||
protected boolean enabled;
|
protected boolean enabled;
|
||||||
protected boolean totp;
|
protected boolean totp;
|
||||||
|
@ -50,6 +51,14 @@ public class UserRepresentation {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedTimestamp(Long createdTimestamp) {
|
||||||
|
this.createdTimestamp = createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,13 @@ public interface UserModel {
|
||||||
|
|
||||||
void setUsername(String username);
|
void setUsername(String username);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get timestamp of user creation. May be null for old users created before this feature introduction.
|
||||||
|
*/
|
||||||
|
Long getCreatedTimestamp();
|
||||||
|
|
||||||
|
void setCreatedTimestamp(Long timestamp);
|
||||||
|
|
||||||
boolean isEnabled();
|
boolean isEnabled();
|
||||||
|
|
||||||
boolean isTotp();
|
boolean isTotp();
|
||||||
|
|
|
@ -12,6 +12,7 @@ import java.util.Map;
|
||||||
public class UserEntity extends AbstractIdentifiableEntity {
|
public class UserEntity extends AbstractIdentifiableEntity {
|
||||||
|
|
||||||
private String username;
|
private String username;
|
||||||
|
private Long createdTimestamp;
|
||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
private String email;
|
private String email;
|
||||||
|
@ -37,6 +38,15 @@ public class UserEntity extends AbstractIdentifiableEntity {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
this.createdTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ public class ModelToRepresentation {
|
||||||
UserRepresentation rep = new UserRepresentation();
|
UserRepresentation rep = new UserRepresentation();
|
||||||
rep.setId(user.getId());
|
rep.setId(user.getId());
|
||||||
rep.setUsername(user.getUsername());
|
rep.setUsername(user.getUsername());
|
||||||
|
rep.setCreatedTimestamp(user.getCreatedTimestamp());
|
||||||
rep.setLastName(user.getLastName());
|
rep.setLastName(user.getLastName());
|
||||||
rep.setFirstName(user.getFirstName());
|
rep.setFirstName(user.getFirstName());
|
||||||
rep.setEmail(user.getEmail());
|
rep.setEmail(user.getEmail());
|
||||||
|
|
|
@ -798,6 +798,7 @@ public class RepresentationToModel {
|
||||||
// Import users just to user storage. Don't federate
|
// Import users just to user storage. Don't federate
|
||||||
UserModel user = session.userStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
|
UserModel user = session.userStorage().addUser(newRealm, userRep.getId(), userRep.getUsername(), false, false);
|
||||||
user.setEnabled(userRep.isEnabled());
|
user.setEnabled(userRep.isEnabled());
|
||||||
|
user.setCreatedTimestamp(userRep.getCreatedTimestamp());
|
||||||
user.setEmail(userRep.getEmail());
|
user.setEmail(userRep.getEmail());
|
||||||
user.setEmailVerified(userRep.isEmailVerified());
|
user.setEmailVerified(userRep.isEmailVerified());
|
||||||
user.setFirstName(userRep.getFirstName());
|
user.setFirstName(userRep.getFirstName());
|
||||||
|
|
|
@ -225,4 +225,14 @@ public class UserModelDelegate implements UserModel {
|
||||||
public UserModel getDelegate() {
|
public UserModel getDelegate() {
|
||||||
return delegate;
|
return delegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCreatedTimestamp(){
|
||||||
|
return delegate.getCreatedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreatedTimestamp(Long timestamp){
|
||||||
|
delegate.setCreatedTimestamp(timestamp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -305,6 +305,7 @@ public class FileUserProvider implements UserProvider {
|
||||||
|
|
||||||
UserEntity userEntity = new UserEntity();
|
UserEntity userEntity = new UserEntity();
|
||||||
userEntity.setId(userId);
|
userEntity.setId(userId);
|
||||||
|
userEntity.setCreatedTimestamp(System.currentTimeMillis());
|
||||||
userEntity.setUsername(username);
|
userEntity.setUsername(username);
|
||||||
// Compatibility with JPA model, which has user disabled by default
|
// Compatibility with JPA model, which has user disabled by default
|
||||||
// userEntity.setEnabled(true);
|
// userEntity.setEnabled(true);
|
||||||
|
|
|
@ -98,6 +98,16 @@ public class UserAdapter implements UserModel, Comparable {
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return user.getCreatedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
user.setCreatedTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return user.isEnabled();
|
return user.isEnabled();
|
||||||
|
|
|
@ -1,21 +1,21 @@
|
||||||
package org.keycloak.models.cache;
|
package org.keycloak.models.cache;
|
||||||
|
|
||||||
import org.keycloak.models.ClientModel;
|
|
||||||
import org.keycloak.models.UserConsentModel;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.RealmModel;
|
|
||||||
import org.keycloak.models.RoleContainerModel;
|
|
||||||
import org.keycloak.models.RoleModel;
|
|
||||||
import org.keycloak.models.UserCredentialModel;
|
|
||||||
import org.keycloak.models.UserCredentialValueModel;
|
|
||||||
import org.keycloak.models.UserModel;
|
|
||||||
import org.keycloak.models.cache.entities.CachedUser;
|
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import org.keycloak.models.ClientModel;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.RoleContainerModel;
|
||||||
|
import org.keycloak.models.RoleModel;
|
||||||
|
import org.keycloak.models.UserConsentModel;
|
||||||
|
import org.keycloak.models.UserCredentialModel;
|
||||||
|
import org.keycloak.models.UserCredentialValueModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.models.cache.entities.CachedUser;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
* @author <a href="mailto:bill@burkecentral.com">Bill Burke</a>
|
||||||
* @version $Revision: 1 $
|
* @version $Revision: 1 $
|
||||||
|
@ -38,18 +38,22 @@ public class UserAdapter implements UserModel {
|
||||||
if (updated == null) {
|
if (updated == null) {
|
||||||
userProviderCache.registerUserInvalidation(realm, getId());
|
userProviderCache.registerUserInvalidation(realm, getId());
|
||||||
updated = userProviderCache.getDelegate().getUserById(getId(), realm);
|
updated = userProviderCache.getDelegate().getUserById(getId(), realm);
|
||||||
if (updated == null) throw new IllegalStateException("Not found in database");
|
if (updated == null)
|
||||||
|
throw new IllegalStateException("Not found in database");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getId() {
|
public String getId() {
|
||||||
if (updated != null) return updated.getId();
|
if (updated != null)
|
||||||
|
return updated.getId();
|
||||||
return cached.getId();
|
return cached.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUsername() {
|
public String getUsername() {
|
||||||
if (updated != null) return updated.getUsername();
|
if (updated != null)
|
||||||
|
return updated.getUsername();
|
||||||
return cached.getUsername();
|
return cached.getUsername();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,15 +63,28 @@ public class UserAdapter implements UserModel {
|
||||||
updated.setUsername(username);
|
updated.setUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
// get from cached always as it is immutable
|
||||||
|
return cached.getCreatedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
// nothing to do as this value is immutable
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
if (updated != null) return updated.isEnabled();
|
if (updated != null)
|
||||||
|
return updated.isEnabled();
|
||||||
return cached.isEnabled();
|
return cached.isEnabled();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTotp() {
|
public boolean isTotp() {
|
||||||
if (updated != null) return updated.isTotp();
|
if (updated != null)
|
||||||
|
return updated.isTotp();
|
||||||
return cached.isTotp();
|
return cached.isTotp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -91,19 +108,22 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getAttribute(String name) {
|
public String getAttribute(String name) {
|
||||||
if (updated != null) return updated.getAttribute(name);
|
if (updated != null)
|
||||||
|
return updated.getAttribute(name);
|
||||||
return cached.getAttributes().get(name);
|
return cached.getAttributes().get(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, String> getAttributes() {
|
public Map<String, String> getAttributes() {
|
||||||
if (updated != null) return updated.getAttributes();
|
if (updated != null)
|
||||||
|
return updated.getAttributes();
|
||||||
return cached.getAttributes();
|
return cached.getAttributes();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<String> getRequiredActions() {
|
public Set<String> getRequiredActions() {
|
||||||
if (updated != null) return updated.getRequiredActions();
|
if (updated != null)
|
||||||
|
return updated.getRequiredActions();
|
||||||
return cached.getRequiredActions();
|
return cached.getRequiredActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +153,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
if (updated != null) return updated.getFirstName();
|
if (updated != null)
|
||||||
|
return updated.getFirstName();
|
||||||
return cached.getFirstName();
|
return cached.getFirstName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -145,7 +166,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getLastName() {
|
public String getLastName() {
|
||||||
if (updated != null) return updated.getLastName();
|
if (updated != null)
|
||||||
|
return updated.getLastName();
|
||||||
return cached.getLastName();
|
return cached.getLastName();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +179,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getEmail() {
|
public String getEmail() {
|
||||||
if (updated != null) return updated.getEmail();
|
if (updated != null)
|
||||||
|
return updated.getEmail();
|
||||||
return cached.getEmail();
|
return cached.getEmail();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +192,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEmailVerified() {
|
public boolean isEmailVerified() {
|
||||||
if (updated != null) return updated.isEmailVerified();
|
if (updated != null)
|
||||||
|
return updated.isEmailVerified();
|
||||||
return cached.isEmailVerified();
|
return cached.isEmailVerified();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -193,7 +217,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
public List<UserCredentialValueModel> getCredentialsDirectly() {
|
||||||
if (updated != null) return updated.getCredentialsDirectly();
|
if (updated != null)
|
||||||
|
return updated.getCredentialsDirectly();
|
||||||
return cached.getCredentials();
|
return cached.getCredentials();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -205,7 +230,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getFederationLink() {
|
public String getFederationLink() {
|
||||||
if (updated != null) return updated.getFederationLink();
|
if (updated != null)
|
||||||
|
return updated.getFederationLink();
|
||||||
return cached.getFederationLink();
|
return cached.getFederationLink();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -213,11 +239,12 @@ public class UserAdapter implements UserModel {
|
||||||
public void setFederationLink(String link) {
|
public void setFederationLink(String link) {
|
||||||
getDelegateForUpdate();
|
getDelegateForUpdate();
|
||||||
updated.setFederationLink(link);
|
updated.setFederationLink(link);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<RoleModel> getRealmRoleMappings() {
|
public Set<RoleModel> getRealmRoleMappings() {
|
||||||
if (updated != null) return updated.getRealmRoleMappings();
|
if (updated != null)
|
||||||
|
return updated.getRealmRoleMappings();
|
||||||
Set<RoleModel> roleMappings = getRoleMappings();
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
Set<RoleModel> realmMappings = new HashSet<RoleModel>();
|
Set<RoleModel> realmMappings = new HashSet<RoleModel>();
|
||||||
for (RoleModel role : roleMappings) {
|
for (RoleModel role : roleMappings) {
|
||||||
|
@ -233,7 +260,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
public Set<RoleModel> getClientRoleMappings(ClientModel app) {
|
||||||
if (updated != null) return updated.getClientRoleMappings(app);
|
if (updated != null)
|
||||||
|
return updated.getClientRoleMappings(app);
|
||||||
Set<RoleModel> roleMappings = getRoleMappings();
|
Set<RoleModel> roleMappings = getRoleMappings();
|
||||||
Set<RoleModel> appMappings = new HashSet<RoleModel>();
|
Set<RoleModel> appMappings = new HashSet<RoleModel>();
|
||||||
for (RoleModel role : roleMappings) {
|
for (RoleModel role : roleMappings) {
|
||||||
|
@ -249,12 +277,15 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean hasRole(RoleModel role) {
|
public boolean hasRole(RoleModel role) {
|
||||||
if (updated != null) return updated.hasRole(role);
|
if (updated != null)
|
||||||
if (cached.getRoleMappings().contains(role.getId())) return true;
|
return updated.hasRole(role);
|
||||||
|
if (cached.getRoleMappings().contains(role.getId()))
|
||||||
|
return true;
|
||||||
|
|
||||||
Set<RoleModel> mappings = getRoleMappings();
|
Set<RoleModel> mappings = getRoleMappings();
|
||||||
for (RoleModel mapping: mappings) {
|
for (RoleModel mapping : mappings) {
|
||||||
if (mapping.hasRole(role)) return true;
|
if (mapping.hasRole(role))
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -267,7 +298,8 @@ public class UserAdapter implements UserModel {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Set<RoleModel> getRoleMappings() {
|
public Set<RoleModel> getRoleMappings() {
|
||||||
if (updated != null) return updated.getRoleMappings();
|
if (updated != null)
|
||||||
|
return updated.getRoleMappings();
|
||||||
Set<RoleModel> roles = new HashSet<RoleModel>();
|
Set<RoleModel> roles = new HashSet<RoleModel>();
|
||||||
for (String id : cached.getRoleMappings()) {
|
for (String id : cached.getRoleMappings()) {
|
||||||
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
RoleModel roleById = keycloakSession.realms().getRoleById(id, realm);
|
||||||
|
|
|
@ -21,6 +21,7 @@ public class CachedUser implements Serializable {
|
||||||
private String id;
|
private String id;
|
||||||
private String realm;
|
private String realm;
|
||||||
private String username;
|
private String username;
|
||||||
|
private Long createdTimestamp;
|
||||||
private String firstName;
|
private String firstName;
|
||||||
private String lastName;
|
private String lastName;
|
||||||
private String email;
|
private String email;
|
||||||
|
@ -33,11 +34,11 @@ public class CachedUser implements Serializable {
|
||||||
private Set<String> requiredActions = new HashSet<>();
|
private Set<String> requiredActions = new HashSet<>();
|
||||||
private Set<String> roleMappings = new HashSet<String>();
|
private Set<String> roleMappings = new HashSet<String>();
|
||||||
|
|
||||||
|
|
||||||
public CachedUser(RealmModel realm, UserModel user) {
|
public CachedUser(RealmModel realm, UserModel user) {
|
||||||
this.id = user.getId();
|
this.id = user.getId();
|
||||||
this.realm = realm.getId();
|
this.realm = realm.getId();
|
||||||
this.username = user.getUsername();
|
this.username = user.getUsername();
|
||||||
|
this.createdTimestamp = user.getCreatedTimestamp();
|
||||||
this.firstName = user.getFirstName();
|
this.firstName = user.getFirstName();
|
||||||
this.lastName = user.getLastName();
|
this.lastName = user.getLastName();
|
||||||
this.attributes.putAll(user.getAttributes());
|
this.attributes.putAll(user.getAttributes());
|
||||||
|
@ -65,6 +66,10 @@ public class CachedUser implements Serializable {
|
||||||
return username;
|
return username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ public class JpaUserProvider implements UserProvider {
|
||||||
|
|
||||||
UserEntity entity = new UserEntity();
|
UserEntity entity = new UserEntity();
|
||||||
entity.setId(id);
|
entity.setId(id);
|
||||||
|
entity.setCreatedTimestamp(System.currentTimeMillis());
|
||||||
entity.setUsername(username.toLowerCase());
|
entity.setUsername(username.toLowerCase());
|
||||||
entity.setRealmId(realm.getId());
|
entity.setRealmId(realm.getId());
|
||||||
em.persist(entity);
|
em.persist(entity);
|
||||||
|
|
|
@ -76,6 +76,16 @@ public class UserAdapter implements UserModel {
|
||||||
user.setUsername(username);
|
user.setUsername(username);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return user.getCreatedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
user.setCreatedTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return user.isEnabled();
|
return user.isEnabled();
|
||||||
|
|
|
@ -11,6 +11,7 @@ import javax.persistence.NamedQuery;
|
||||||
import javax.persistence.OneToMany;
|
import javax.persistence.OneToMany;
|
||||||
import javax.persistence.Table;
|
import javax.persistence.Table;
|
||||||
import javax.persistence.UniqueConstraint;
|
import javax.persistence.UniqueConstraint;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
@ -44,6 +45,8 @@ public class UserEntity {
|
||||||
protected String username;
|
protected String username;
|
||||||
@Column(name = "FIRST_NAME")
|
@Column(name = "FIRST_NAME")
|
||||||
protected String firstName;
|
protected String firstName;
|
||||||
|
@Column(name = "CREATED_TIMESTAMP")
|
||||||
|
protected Long createdTimestamp;
|
||||||
@Column(name = "LAST_NAME")
|
@Column(name = "LAST_NAME")
|
||||||
protected String lastName;
|
protected String lastName;
|
||||||
@Column(name = "EMAIL")
|
@Column(name = "EMAIL")
|
||||||
|
@ -90,6 +93,14 @@ public class UserEntity {
|
||||||
this.username = username;
|
this.username = username;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return createdTimestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
createdTimestamp = timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
public String getFirstName() {
|
public String getFirstName() {
|
||||||
return firstName;
|
return firstName;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package org.keycloak.models.mongo.keycloak.adapters;
|
||||||
import com.mongodb.BasicDBObject;
|
import com.mongodb.BasicDBObject;
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.QueryBuilder;
|
import com.mongodb.QueryBuilder;
|
||||||
|
|
||||||
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;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
|
@ -274,6 +275,7 @@ public class MongoUserProvider implements UserProvider {
|
||||||
MongoUserEntity userEntity = new MongoUserEntity();
|
MongoUserEntity userEntity = new MongoUserEntity();
|
||||||
userEntity.setId(id);
|
userEntity.setId(id);
|
||||||
userEntity.setUsername(username);
|
userEntity.setUsername(username);
|
||||||
|
userEntity.setCreatedTimestamp(System.currentTimeMillis());
|
||||||
// Compatibility with JPA model, which has user disabled by default
|
// Compatibility with JPA model, which has user disabled by default
|
||||||
// userEntity.setEnabled(true);
|
// userEntity.setEnabled(true);
|
||||||
userEntity.setRealmId(realm.getId());
|
userEntity.setRealmId(realm.getId());
|
||||||
|
|
|
@ -4,6 +4,7 @@ import static org.keycloak.models.utils.Pbkdf2PasswordEncoder.getSalt;
|
||||||
|
|
||||||
import com.mongodb.DBObject;
|
import com.mongodb.DBObject;
|
||||||
import com.mongodb.QueryBuilder;
|
import com.mongodb.QueryBuilder;
|
||||||
|
|
||||||
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
import org.keycloak.connections.mongo.api.context.MongoStoreInvocationContext;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.ProtocolMapperModel;
|
import org.keycloak.models.ProtocolMapperModel;
|
||||||
|
@ -71,6 +72,16 @@ public class UserAdapter extends AbstractMongoAdapter<MongoUserEntity> implement
|
||||||
updateUser();
|
updateUser();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Long getCreatedTimestamp() {
|
||||||
|
return user.getCreatedTimestamp();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setCreatedTimestamp(Long timestamp) {
|
||||||
|
user.setCreatedTimestamp(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isEnabled() {
|
public boolean isEnabled() {
|
||||||
return user.isEnabled();
|
return user.isEnabled();
|
||||||
|
|
|
@ -686,6 +686,9 @@ public abstract class AbstractIdentityProviderTest {
|
||||||
UserModel federatedUser = getFederatedUser();
|
UserModel federatedUser = getFederatedUser();
|
||||||
|
|
||||||
assertNotNull(federatedUser);
|
assertNotNull(federatedUser);
|
||||||
|
assertNotNull(federatedUser.getCreatedTimestamp());
|
||||||
|
// test that timestamp is current with 10s tollerance
|
||||||
|
Assert.assertTrue((System.currentTimeMillis() - federatedUser.getCreatedTimestamp()) < 10000);
|
||||||
|
|
||||||
doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
|
doAssertFederatedUser(federatedUser, identityProviderModel, expectedEmail, isProfileUpdateExpected);
|
||||||
|
|
||||||
|
|
|
@ -26,11 +26,15 @@ import org.junit.ClassRule;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.keycloak.events.Details;
|
import org.keycloak.events.Details;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.PasswordPolicy;
|
import org.keycloak.models.PasswordPolicy;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
|
import org.keycloak.models.UserModel;
|
||||||
|
import org.keycloak.representations.IDToken;
|
||||||
import org.keycloak.services.managers.RealmManager;
|
import org.keycloak.services.managers.RealmManager;
|
||||||
import org.keycloak.testsuite.AssertEvents;
|
import org.keycloak.testsuite.AssertEvents;
|
||||||
import org.keycloak.testsuite.OAuthClient;
|
import org.keycloak.testsuite.OAuthClient;
|
||||||
|
import org.keycloak.testsuite.broker.util.UserSessionStatusServlet.UserSessionStatus;
|
||||||
import org.keycloak.testsuite.pages.AppPage;
|
import org.keycloak.testsuite.pages.AppPage;
|
||||||
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
import org.keycloak.testsuite.pages.AppPage.RequestType;
|
||||||
import org.keycloak.testsuite.pages.LoginPage;
|
import org.keycloak.testsuite.pages.LoginPage;
|
||||||
|
@ -191,6 +195,22 @@ public class RegisterTest {
|
||||||
|
|
||||||
String userId = events.expectRegister("registerUserSuccess", "registerUserSuccess@email").assertEvent().getUserId();
|
String userId = events.expectRegister("registerUserSuccess", "registerUserSuccess@email").assertEvent().getUserId();
|
||||||
events.expectLogin().detail("username", "registerusersuccess").user(userId).assertEvent();
|
events.expectLogin().detail("username", "registerusersuccess").user(userId).assertEvent();
|
||||||
|
|
||||||
|
UserModel user = getUser(userId);
|
||||||
|
Assert.assertNotNull(user);
|
||||||
|
Assert.assertNotNull(user.getCreatedTimestamp());
|
||||||
|
// test that timestamp is current with 10s tollerance
|
||||||
|
Assert.assertTrue((System.currentTimeMillis() - user.getCreatedTimestamp()) < 10000);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected UserModel getUser(String userId) {
|
||||||
|
KeycloakSession samlServerSession = keycloakRule.startSession();
|
||||||
|
try {
|
||||||
|
RealmModel brokerRealm = samlServerSession.realms().getRealm("test");
|
||||||
|
return samlServerSession.users().getUserById(userId, brokerRealm);
|
||||||
|
} finally {
|
||||||
|
keycloakRule.stopSession(samlServerSession, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -251,6 +271,12 @@ public class RegisterTest {
|
||||||
|
|
||||||
String userId = events.expectRegister("registerUserSuccessE@email", "registerUserSuccessE@email").assertEvent().getUserId();
|
String userId = events.expectRegister("registerUserSuccessE@email", "registerUserSuccessE@email").assertEvent().getUserId();
|
||||||
events.expectLogin().detail("username", "registerusersuccesse@email").user(userId).assertEvent();
|
events.expectLogin().detail("username", "registerusersuccesse@email").user(userId).assertEvent();
|
||||||
|
|
||||||
|
UserModel user = getUser(userId);
|
||||||
|
Assert.assertNotNull(user);
|
||||||
|
Assert.assertNotNull(user.getCreatedTimestamp());
|
||||||
|
// test that timestamp is current with 10s tollerance
|
||||||
|
Assert.assertTrue((System.currentTimeMillis() - user.getCreatedTimestamp()) < 10000);
|
||||||
} finally {
|
} finally {
|
||||||
configureRelamRegistrationEmailAsUsername(false);
|
configureRelamRegistrationEmailAsUsername(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -117,6 +117,8 @@ public class ImportTest extends AbstractModelTest {
|
||||||
|
|
||||||
// Test role mappings
|
// Test role mappings
|
||||||
UserModel admin = session.users().getUserByUsername("admin", realm);
|
UserModel admin = session.users().getUserByUsername("admin", realm);
|
||||||
|
// user without creation timestamp in import
|
||||||
|
Assert.assertNull(admin.getCreatedTimestamp());
|
||||||
Set<RoleModel> allRoles = admin.getRoleMappings();
|
Set<RoleModel> allRoles = admin.getRoleMappings();
|
||||||
Assert.assertEquals(3, allRoles.size());
|
Assert.assertEquals(3, allRoles.size());
|
||||||
Assert.assertTrue(allRoles.contains(realm.getRole("admin")));
|
Assert.assertTrue(allRoles.contains(realm.getRole("admin")));
|
||||||
|
@ -124,6 +126,8 @@ public class ImportTest extends AbstractModelTest {
|
||||||
Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-admin")));
|
Assert.assertTrue(allRoles.contains(otherApp.getRole("otherapp-admin")));
|
||||||
|
|
||||||
UserModel wburke = session.users().getUserByUsername("wburke", realm);
|
UserModel wburke = session.users().getUserByUsername("wburke", realm);
|
||||||
|
// user with creation timestamp in import
|
||||||
|
Assert.assertEquals(new Long(123654), wburke.getCreatedTimestamp());
|
||||||
allRoles = wburke.getRoleMappings();
|
allRoles = wburke.getRoleMappings();
|
||||||
Assert.assertEquals(2, allRoles.size());
|
Assert.assertEquals(2, allRoles.size());
|
||||||
Assert.assertFalse(allRoles.contains(realm.getRole("admin")));
|
Assert.assertFalse(allRoles.contains(realm.getRole("admin")));
|
||||||
|
@ -132,6 +136,10 @@ public class ImportTest extends AbstractModelTest {
|
||||||
|
|
||||||
Assert.assertEquals(0, wburke.getRealmRoleMappings().size());
|
Assert.assertEquals(0, wburke.getRealmRoleMappings().size());
|
||||||
|
|
||||||
|
UserModel loginclient = session.users().getUserByUsername("loginclient", realm);
|
||||||
|
// user with creation timestamp as string in import
|
||||||
|
Assert.assertEquals(new Long(123655), loginclient.getCreatedTimestamp());
|
||||||
|
|
||||||
Set<RoleModel> realmRoles = admin.getRealmRoleMappings();
|
Set<RoleModel> realmRoles = admin.getRealmRoleMappings();
|
||||||
Assert.assertEquals(1, realmRoles.size());
|
Assert.assertEquals(1, realmRoles.size());
|
||||||
Assert.assertEquals("admin", realmRoles.iterator().next().getName());
|
Assert.assertEquals("admin", realmRoles.iterator().next().getName());
|
||||||
|
|
|
@ -8,6 +8,8 @@ import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.UserModel;
|
import org.keycloak.models.UserModel;
|
||||||
import org.keycloak.models.UserModel.RequiredAction;
|
import org.keycloak.models.UserModel.RequiredAction;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -26,6 +28,9 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
user.setFirstName("first-name");
|
user.setFirstName("first-name");
|
||||||
user.setLastName("last-name");
|
user.setLastName("last-name");
|
||||||
user.setEmail("email");
|
user.setEmail("email");
|
||||||
|
assertNotNull(user.getCreatedTimestamp());
|
||||||
|
// test that timestamp is current with 10s tollerance
|
||||||
|
Assert.assertTrue((System.currentTimeMillis() - user.getCreatedTimestamp()) < 10000);
|
||||||
|
|
||||||
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
user.addRequiredAction(RequiredAction.CONFIGURE_TOTP);
|
||||||
user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
user.addRequiredAction(RequiredAction.UPDATE_PASSWORD);
|
||||||
|
@ -139,6 +144,7 @@ public class UserModelTest extends AbstractModelTest {
|
||||||
|
|
||||||
public static void assertEquals(UserModel expected, UserModel actual) {
|
public static void assertEquals(UserModel expected, UserModel actual) {
|
||||||
Assert.assertEquals(expected.getUsername(), actual.getUsername());
|
Assert.assertEquals(expected.getUsername(), actual.getUsername());
|
||||||
|
Assert.assertEquals(expected.getCreatedTimestamp(), actual.getCreatedTimestamp());
|
||||||
Assert.assertEquals(expected.getFirstName(), actual.getFirstName());
|
Assert.assertEquals(expected.getFirstName(), actual.getFirstName());
|
||||||
Assert.assertEquals(expected.getLastName(), actual.getLastName());
|
Assert.assertEquals(expected.getLastName(), actual.getLastName());
|
||||||
|
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
{
|
{
|
||||||
"username": "wburke",
|
"username": "wburke",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
|
"createdTimestamp" : 123654,
|
||||||
"attributes": {
|
"attributes": {
|
||||||
"email": "bburke@redhat.com"
|
"email": "bburke@redhat.com"
|
||||||
},
|
},
|
||||||
|
@ -71,6 +72,7 @@
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"username": "loginclient",
|
"username": "loginclient",
|
||||||
|
"createdTimestamp" : "123655",
|
||||||
"enabled": true,
|
"enabled": true,
|
||||||
"credentials": [
|
"credentials": [
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue