diff --git a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java index fca9641fc4..c4181653fc 100644 --- a/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/loginFailure/MapUserLoginFailureEntity.java @@ -56,11 +56,6 @@ public interface MapUserLoginFailureEntity extends AbstractEntity, UpdatableEnti setLastFailure(null); setLastIPFailure(null); } - - @Override - public String toString() { - return String.format("%s@%08x", getId(), hashCode()); - } } String getRealmId(); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java index 12f0eebccf..fd35bb085c 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/ConcurrentHashMapStorageProviderFactory.java @@ -67,6 +67,10 @@ import org.keycloak.models.map.user.MapUserCredentialEntityImpl; import org.keycloak.models.map.user.MapUserEntityImpl; import org.keycloak.models.map.user.MapUserFederatedIdentityEntityImpl; import org.keycloak.models.map.storage.ModelEntityUtil; +import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity; +import org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntityImpl; +import org.keycloak.models.map.userSession.MapUserSessionEntity; +import org.keycloak.models.map.userSession.MapUserSessionEntityImpl; import org.keycloak.provider.EnvironmentDependentProviderFactory; import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; @@ -131,6 +135,8 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide .constructor(MapRootAuthenticationSessionEntity.class, MapRootAuthenticationSessionEntityImpl::new) .constructor(MapAuthenticationSessionEntity.class, MapAuthenticationSessionEntityImpl::new) .constructor(MapUserLoginFailureEntity.class, MapUserLoginFailureEntityImpl::new) + .constructor(MapUserSessionEntity.class, MapUserSessionEntityImpl::new) + .constructor(MapAuthenticatedClientSessionEntity.class, MapAuthenticatedClientSessionEntityImpl::new) .build(); private static final Map KEY_CONVERTERS = new HashMap<>(); diff --git a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java index 24e7ec5788..082d0dec1e 100644 --- a/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java +++ b/model/map/src/main/java/org/keycloak/models/map/storage/chm/MapFieldPredicates.java @@ -491,7 +491,7 @@ public class MapFieldPredicates { private static MapModelCriteriaBuilder checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder mcb, Operator op, Object[] values) { String clientId = ensureEqSingleValue(UserSessionModel.SearchableFields.CLIENT_ID, "client_id", op, values); - Function getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId)); + Function getter = use -> (use.getAuthenticatedClientSession(clientId) != null); return mcb.fieldCompare(Boolean.TRUE::equals, getter); } diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java index bc18422c76..e4936019ad 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionAdapter.java @@ -21,6 +21,7 @@ import org.keycloak.models.KeycloakSession; import org.keycloak.models.RealmModel; import org.keycloak.models.UserSessionModel; +import java.util.Collections; import java.util.Map; /** @@ -40,7 +41,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe @Override public int getTimestamp() { - return entity.getTimestamp(); + Integer timestamp = entity.getTimestamp(); + return timestamp != null ? timestamp : 0; } @Override @@ -65,7 +67,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe @Override public int getCurrentRefreshTokenUseCount() { - return entity.getCurrentRefreshTokenUseCount(); + Integer currentRefreshTokenUseCount = entity.getCurrentRefreshTokenUseCount(); + return currentRefreshTokenUseCount != null ? currentRefreshTokenUseCount : 0; } @Override @@ -75,7 +78,7 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe @Override public String getNote(String name) { - return (name != null) ? entity.getNotes().get(name) : null; + return (name != null) ? entity.getNote(name) : null; } @Override @@ -84,7 +87,7 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe if (value == null) { entity.removeNote(name); } else { - entity.addNote(name, value); + entity.setNote(name, value); } } } @@ -98,7 +101,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe @Override public Map getNotes() { - return entity.getNotes(); + Map notes = entity.getNotes(); + return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java index 33ad54b89b..bd6e5633f7 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapAuthenticatedClientSessionEntity.java @@ -16,184 +16,75 @@ */ package org.keycloak.models.map.userSession; -import org.keycloak.common.util.Time; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; /** * @author Martin Kanis */ -public class MapAuthenticatedClientSessionEntity extends UpdatableEntity.Impl implements AbstractEntity { +@GenerateEntityImplementations( + inherits = "org.keycloak.models.map.userSession.MapAuthenticatedClientSessionEntity.AbstractAuthenticatedClientSessionEntity" +) +@DeepCloner.Root +public interface MapAuthenticatedClientSessionEntity extends AbstractEntity, UpdatableEntity { - private String id; - private String userSessionId; - private String realmId; - private String clientId; + public abstract class AbstractAuthenticatedClientSessionEntity extends UpdatableEntity.Impl implements MapAuthenticatedClientSessionEntity { - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ + private String id; - private String authMethod; - private String redirectUri; - private volatile int timestamp; - private long expiration; - private String action; + @Override + public String getId() { + return this.id; + } - private Map notes = new ConcurrentHashMap<>(); - - private String currentRefreshToken; - private int currentRefreshTokenUseCount; - - private boolean offline; - - public MapAuthenticatedClientSessionEntity() {} - - public MapAuthenticatedClientSessionEntity(String id, String userSessionId, String realmId, String clientId, boolean offline) { - this.id = id; - this.userSessionId = userSessionId; - this.realmId = realmId; - this.clientId = clientId; - this.offline = offline; - this.timestamp = Time.currentTime(); + @Override + public void setId(String id) { + if (this.id != null) throw new IllegalStateException("Id cannot be changed"); + this.id = id; + this.updated |= id != null; + } } - @Override - public String getId() { - return this.id; - } + String getRealmId(); + void setRealmId(String realmId); - @Override - public void setId(String id) { - if (this.id != null) throw new IllegalStateException("Id cannot be changed"); - this.id = id; - this.updated |= id != null; - } + String getClientId(); + void setClientId(String clientId); - public String getRealmId() { - return realmId; - } + String getUserSessionId(); + void setUserSessionId(String userSessionId); - public void setRealmId(String realmId) { - this.updated |= !Objects.equals(this.realmId, realmId); - this.realmId = realmId; - } + String getAuthMethod(); + void setAuthMethod(String authMethod); - public String getClientId() { - return clientId; - } + String getRedirectUri(); + void setRedirectUri(String redirectUri); - public void setClientId(String clientId) { - this.updated |= !Objects.equals(this.clientId, clientId); - this.clientId = clientId; - } + Integer getTimestamp(); + void setTimestamp(Integer timestamp); - public String getUserSessionId() { - return userSessionId; - } + Long getExpiration(); + void setExpiration(Long expiration); - public void setUserSessionId(String userSessionId) { - this.updated |= !Objects.equals(this.userSessionId, userSessionId); - this.userSessionId = userSessionId; - } + String getAction(); + void setAction(String action); - public String getAuthMethod() { - return authMethod; - } + Map getNotes(); + void setNotes(Map notes); + String getNote(String name); + Boolean removeNote(String name); + void setNote(String name, String value); - public void setAuthMethod(String authMethod) { - this.updated |= !Objects.equals(this.authMethod, authMethod); - this.authMethod = authMethod; - } + String getCurrentRefreshToken(); + void setCurrentRefreshToken(String currentRefreshToken); - public String getRedirectUri() { - return redirectUri; - } + Integer getCurrentRefreshTokenUseCount(); + void setCurrentRefreshTokenUseCount(Integer currentRefreshTokenUseCount); - public void setRedirectUri(String redirectUri) { - this.updated |= !Objects.equals(this.redirectUri, redirectUri); - this.redirectUri = redirectUri; - } - - public int getTimestamp() { - return timestamp; - } - - public void setTimestamp(int timestamp) { - this.updated |= this.timestamp != timestamp; - this.timestamp = timestamp; - } - - public long getExpiration() { - return expiration; - } - - public void setExpiration(long expiration) { - this.updated |= this.expiration != expiration; - this.expiration = expiration; - } - - public String getAction() { - return action; - } - - public void setAction(String action) { - this.updated |= !Objects.equals(this.action, action); - this.action = action; - } - - public Map getNotes() { - return notes; - } - - public void setNotes(Map notes) { - this.updated |= !Objects.equals(this.notes, notes); - this.notes = notes; - } - - public String removeNote(String name) { - String note = this.notes.remove(name); - this.updated |= note != null; - return note; - } - - public void addNote(String name, String value) { - this.updated |= !Objects.equals(this.notes.put(name, value), value); - } - - public String getCurrentRefreshToken() { - return currentRefreshToken; - } - - public void setCurrentRefreshToken(String currentRefreshToken) { - this.updated |= !Objects.equals(this.currentRefreshToken, currentRefreshToken); - this.currentRefreshToken = currentRefreshToken; - } - - public int getCurrentRefreshTokenUseCount() { - return currentRefreshTokenUseCount; - } - - public void setCurrentRefreshTokenUseCount(int currentRefreshTokenUseCount) { - this.updated |= this.currentRefreshTokenUseCount != currentRefreshTokenUseCount; - this.currentRefreshTokenUseCount = currentRefreshTokenUseCount; - } - - public boolean isOffline() { - return offline; - } - - public void setOffline(boolean offline) { - this.updated |= this.offline != offline; - this.offline = offline; - } - - @Override - public String toString() { - return String.format("%s@%08x", getId(), hashCode()); - } + Boolean isOffline(); + void setOffline(Boolean offline); } diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java index 9dab9ec41a..6fa34c32ca 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionAdapter.java @@ -83,17 +83,20 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { @Override public boolean isRememberMe() { - return entity.isRememberMe(); + Boolean rememberMe = entity.isRememberMe(); + return rememberMe != null ? rememberMe : false; } @Override public int getStarted() { - return entity.getStarted(); + Integer started = entity.getStarted(); + return started != null ? started : 0; } @Override public int getLastSessionRefresh() { - return entity.getLastSessionRefresh(); + Integer lastSessionRefresh = entity.getLastSessionRefresh(); + return lastSessionRefresh != null ? lastSessionRefresh : 0; } @Override @@ -103,7 +106,8 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { @Override public boolean isOffline() { - return entity.isOffline(); + Boolean offline = entity.isOffline(); + return offline != null ? offline : false; } @Override @@ -111,8 +115,13 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { Map result = new HashMap<>(); List removedClientUUIDS = new LinkedList<>(); - // to avoid concurrentModificationException - Map authenticatedClientSessions = new HashMap<>(entity.getAuthenticatedClientSessions()); + Map authenticatedClientSessions = entity.getAuthenticatedClientSessions(); + if (authenticatedClientSessions == null) { + return Collections.emptyMap(); + } else { + // to avoid concurrentModificationException + authenticatedClientSessions = new HashMap<>(authenticatedClientSessions); + } authenticatedClientSessions.forEach((clientUUID, clientSessionId) -> { ClientModel client = realm.getClientById(clientUUID); @@ -135,7 +144,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { @Override public AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) { - String clientSessionId = entity.getAuthenticatedClientSessions().get(clientUUID); + String clientSessionId = entity.getAuthenticatedClientSession(clientUUID); if (clientSessionId == null) { return null; @@ -155,7 +164,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { @Override public String getNote(String name) { - return (name != null) ? entity.getNotes().get(name) : null; + return (name != null) ? entity.getNote(name) : null; } @Override @@ -164,7 +173,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { if (value == null) { entity.removeNote(name); } else { - entity.addNote(name, value); + entity.setNote(name, value); } } } @@ -178,7 +187,8 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { @Override public Map getNotes() { - return entity.getNotes(); + Map notes = entity.getNotes(); + return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes); } @Override @@ -212,9 +222,9 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel { String correspondingSessionId = entity.getNote(CORRESPONDING_SESSION_ID); entity.setNotes(new ConcurrentHashMap<>()); if (correspondingSessionId != null) - entity.addNote(CORRESPONDING_SESSION_ID, correspondingSessionId); + entity.setNote(CORRESPONDING_SESSION_ID, correspondingSessionId); - entity.clearAuthenticatedClientSessions(); + entity.setAuthenticatedClientSessions(null); } @Override diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionEntity.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionEntity.java index edba96570f..65fc3b1d55 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionEntity.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionEntity.java @@ -16,269 +16,93 @@ */ package org.keycloak.models.map.userSession; -import org.keycloak.common.util.Time; -import org.keycloak.models.RealmModel; -import org.keycloak.models.UserModel; import org.keycloak.models.UserSessionModel; +import org.keycloak.models.map.annotations.GenerateEntityImplementations; import org.keycloak.models.map.common.AbstractEntity; +import org.keycloak.models.map.common.DeepCloner; import org.keycloak.models.map.common.UpdatableEntity; + import java.util.Map; -import java.util.Objects; -import java.util.concurrent.ConcurrentHashMap; /** * @author Martin Kanis */ -public class MapUserSessionEntity extends UpdatableEntity.Impl implements AbstractEntity { - private String id; +@GenerateEntityImplementations( + inherits = "org.keycloak.models.map.userSession.MapUserSessionEntity.AbstractUserSessionEntity" +) +@DeepCloner.Root +public interface MapUserSessionEntity extends AbstractEntity, UpdatableEntity { - private String realmId; + public abstract class AbstractUserSessionEntity extends UpdatableEntity.Impl implements MapUserSessionEntity { - /** - * Flag signalizing that any of the setters has been meaningfully used. - */ + private String id; - private String userId; + @Override + public String getId() { + return this.id; + } - private String brokerSessionId; - private String brokerUserId; - - private String loginUsername; - - private String ipAddress; - - private String authMethod; - - private boolean rememberMe; - - private int started; - - private int lastSessionRefresh; - - private long expiration; - - private Map notes = new ConcurrentHashMap<>(); - - private UserSessionModel.State state; - - private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT; - - private Map authenticatedClientSessions = new ConcurrentHashMap<>(); - - private boolean offline; - - public MapUserSessionEntity() {} - - public MapUserSessionEntity(String id, String realmId) { - this.id = id; - this.realmId = realmId; + @Override + public void setId(String id) { + if (this.id != null) throw new IllegalStateException("Id cannot be changed"); + this.id = id; + this.updated |= id != null; + } } - public MapUserSessionEntity(String id, RealmModel realm, UserModel user, String loginUsername, String ipAddress, - String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId, - boolean offline) { - this.id = id; - this.realmId = realm.getId(); - this.userId = user.getId(); - this.loginUsername = loginUsername; - this.ipAddress = ipAddress; - this.authMethod = authMethod; - this.rememberMe = rememberMe; - this.brokerSessionId = brokerSessionId; - this.brokerUserId = brokerUserId; - this.started = Time.currentTime(); - this.lastSessionRefresh = started; - this.offline = offline; - } + String getRealmId(); + void setRealmId(String realmId); - @Override - public String getId() { - return this.id; - } + String getUserId(); + void setUserId(String userId); - @Override - public void setId(String id) { - if (this.id != null) throw new IllegalStateException("Id cannot be changed"); - this.id = id; - this.updated |= id != null; - } + String getBrokerSessionId(); + void setBrokerSessionId(String brokerSessionId); - public String getRealmId() { - return realmId; - } + String getBrokerUserId(); + void setBrokerUserId(String brokerUserId); - public void setRealmId(String realmId) { - this.updated |= !Objects.equals(this.realmId, realmId); - this.realmId = realmId; - } + String getLoginUsername(); + void setLoginUsername(String loginUsername); - public String getUserId() { - return userId; - } + String getIpAddress(); + void setIpAddress(String ipAddress); - public void setUserId(String userId) { - this.updated |= !Objects.equals(this.userId, userId); - this.userId = userId; - } + String getAuthMethod(); + void setAuthMethod(String authMethod); - public String getBrokerSessionId() { - return brokerSessionId; - } + Boolean isRememberMe(); + void setRememberMe(Boolean rememberMe); - public void setBrokerSessionId(String brokerSessionId) { - this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId); - this.brokerSessionId = brokerSessionId; - } + Integer getStarted(); + void setStarted(Integer started); - public String getBrokerUserId() { - return brokerUserId; - } + Integer getLastSessionRefresh(); - public void setBrokerUserId(String brokerUserId) { - this.updated |= !Objects.equals(this.brokerUserId, brokerUserId); - this.brokerUserId = brokerUserId; - } + void setLastSessionRefresh(Integer lastSessionRefresh); - public String getLoginUsername() { - return loginUsername; - } + Long getExpiration(); + void setExpiration(Long expiration); - public void setLoginUsername(String loginUsername) { - this.updated |= !Objects.equals(this.loginUsername, loginUsername); - this.loginUsername = loginUsername; - } + Map getNotes(); + String getNote(String name); + void setNotes(Map notes); + Boolean removeNote(String name); + void setNote(String name, String value); - public String getIpAddress() { - return ipAddress; - } + UserSessionModel.State getState(); + void setState(UserSessionModel.State state); - public void setIpAddress(String ipAddress) { - this.updated |= !Objects.equals(this.ipAddress, ipAddress); - this.ipAddress = ipAddress; - } + Map getAuthenticatedClientSessions(); + void setAuthenticatedClientSessions(Map authenticatedClientSessions); + String getAuthenticatedClientSession(String clientUUID); + void setAuthenticatedClientSession(String clientUUID, String clientSessionId); + Boolean removeAuthenticatedClientSession(String clientUUID); - public String getAuthMethod() { - return authMethod; - } + Boolean isOffline(); + void setOffline(Boolean offline); - public void setAuthMethod(String authMethod) { - this.updated |= !Objects.equals(this.authMethod, authMethod); - this.authMethod = authMethod; - } - - public boolean isRememberMe() { - return rememberMe; - } - - public void setRememberMe(boolean rememberMe) { - this.updated |= this.rememberMe != rememberMe; - this.rememberMe = rememberMe; - } - - public int getStarted() { - return started; - } - - public void setStarted(int started) { - this.updated |= this.started != started; - this.started = started; - } - - public int getLastSessionRefresh() { - return lastSessionRefresh; - } - - public void setLastSessionRefresh(int lastSessionRefresh) { - this.updated |= this.lastSessionRefresh != lastSessionRefresh; - this.lastSessionRefresh = lastSessionRefresh; - } - - public long getExpiration() { - return expiration; - } - - public void setExpiration(long expiration) { - this.updated |= this.expiration != expiration; - this.expiration = expiration; - } - - public Map getNotes() { - return notes; - } - - public String getNote(String name) { - return notes.get(name); - } - - public void setNotes(Map notes) { - this.updated |= !Objects.equals(this.notes, notes); - this.notes = notes; - } - - public String removeNote(String name) { - String note = this.notes.remove(name); - this.updated |= note != null; - return note; - } - - public void addNote(String name, String value) { - this.updated |= !Objects.equals(this.notes.put(name, value), value); - } - - public UserSessionModel.State getState() { - return state; - } - - public void setState(UserSessionModel.State state) { - this.updated |= !Objects.equals(this.state, state); - this.state = state; - } - - public Map getAuthenticatedClientSessions() { - return authenticatedClientSessions; - } - - public void setAuthenticatedClientSessions(Map authenticatedClientSessions) { - this.updated |= !Objects.equals(this.authenticatedClientSessions, authenticatedClientSessions); - this.authenticatedClientSessions = authenticatedClientSessions; - } - - public void addAuthenticatedClientSession(String clientId, String clientSessionId) { - this.updated |= !Objects.equals(this.authenticatedClientSessions.put(clientId, clientSessionId), clientSessionId); - } - - public String removeAuthenticatedClientSession(String clientId) { - String entity = this.authenticatedClientSessions.remove(clientId); - this.updated |= entity != null; - return entity; - } - - public void clearAuthenticatedClientSessions() { - this.updated |= !authenticatedClientSessions.isEmpty(); - this.authenticatedClientSessions.clear(); - } - - public boolean isOffline() { - return offline; - } - - public void setOffline(boolean offline) { - this.updated |= this.offline != offline; - this.offline = offline; - } - - public UserSessionModel.SessionPersistenceState getPersistenceState() { - return persistenceState; - } - - public void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState) { - this.updated |= !Objects.equals(this.persistenceState, persistenceState); - this.persistenceState = persistenceState; - } - - @Override - public String toString() { - return String.format("%s@%08x", getId(), hashCode()); - } + UserSessionModel.SessionPersistenceState getPersistenceState(); + void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState); } diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java index 3ea94bfa8f..1b99a39d09 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/MapUserSessionProvider.java @@ -81,7 +81,8 @@ public class MapUserSessionProvider implements UserSessionProvider { private Function userEntityToAdapterFunc(RealmModel realm) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller return (origEntity) -> { - if (origEntity.getExpiration() <= Time.currentTime()) { + long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0l; + if (expiration <= Time.currentTime()) { if (Objects.equals(origEntity.getPersistenceState(), TRANSIENT)) { transientUserSessions.remove(origEntity.getId()); } @@ -110,7 +111,8 @@ public class MapUserSessionProvider implements UserSessionProvider { UserSessionModel userSession) { // Clone entity before returning back, to avoid giving away a reference to the live object to the caller return origEntity -> { - if (origEntity.getExpiration() <= Time.currentTime()) { + long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0l; + if (expiration <= Time.currentTime()) { userSession.removeAuthenticatedClientSessions(Arrays.asList(origEntity.getClientId())); clientSessionTx.delete(origEntity.getId()); return null; @@ -141,9 +143,10 @@ public class MapUserSessionProvider implements UserSessionProvider { @Override public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) { - MapAuthenticatedClientSessionEntity entity = - new MapAuthenticatedClientSessionEntity(null, userSession.getId(), realm.getId(), client.getId(), false); - entity.getNotes().put(AuthenticatedClientSessionModel.STARTED_AT_NOTE, String.valueOf(entity.getTimestamp())); + MapAuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionEntityInstance(null, userSession.getId(), + realm.getId(), client.getId(), false); + String started = entity.getTimestamp() != null ? String.valueOf(entity.getTimestamp()) : String.valueOf(0); + entity.setNote(AuthenticatedClientSessionModel.STARTED_AT_NOTE, started); setClientSessionExpiration(entity, realm, client); LOG.tracef("createClientSession(%s, %s, %s)%s", realm, client, userSession, getShortStackTrace()); @@ -156,7 +159,7 @@ public class MapUserSessionProvider implements UserSessionProvider { throw new IllegalStateException("User session entity does not exist: " + userSession.getId()); } - userSessionEntity.addAuthenticatedClientSession(client.getId(), entity.getId()); + userSessionEntity.setAuthenticatedClientSession(client.getId(), entity.getId()); return clientEntityToAdapterFunc(realm, client, userSession).apply(entity); } @@ -204,13 +207,15 @@ public class MapUserSessionProvider implements UserSessionProvider { if (id == null) { id = UUID.randomUUID().toString(); } - entity = new MapUserSessionEntity(id, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false); + entity = createUserSessionEntityInstance(id, realm.getId(), user.getId(), loginUsername, ipAddress, authMethod, + rememberMe, brokerSessionId, brokerUserId, false); transientUserSessions.put(entity.getId(), entity); } else { if (id != null && userSessionTx.read(id) != null) { throw new ModelDuplicateException("User session exists: " + id); } - entity = new MapUserSessionEntity(id, realm, user, loginUsername, ipAddress, authMethod, rememberMe, brokerSessionId, brokerUserId, false); + entity = createUserSessionEntityInstance(id, realm.getId(), user.getId(), loginUsername, ipAddress, authMethod, + rememberMe, brokerSessionId, brokerUserId, false); entity = userSessionTx.create(entity); } @@ -461,14 +466,14 @@ public class MapUserSessionProvider implements UserSessionProvider { MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true); int currentTime = Time.currentTime(); - clientSessionEntity.getNotes().put(AuthenticatedClientSessionModel.STARTED_AT_NOTE, String.valueOf(currentTime)); + clientSessionEntity.setNote(AuthenticatedClientSessionModel.STARTED_AT_NOTE, String.valueOf(currentTime)); clientSessionEntity.setTimestamp(currentTime); setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient()); clientSessionEntity = clientSessionTx.create(clientSessionEntity); Optional userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst(); if (userSessionEntity.isPresent()) { - userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId()); + userSessionEntity.get().setAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId()); } return clientEntityToAdapterFunc(clientSession.getRealm(), @@ -544,8 +549,8 @@ public class MapUserSessionProvider implements UserSessionProvider { persistentUserSessions.stream() .map(pus -> { - MapUserSessionEntity userSessionEntity = new MapUserSessionEntity(null, pus.getRealm(), pus.getUser(), - pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(), + MapUserSessionEntity userSessionEntity = createUserSessionEntityInstance(null, pus.getRealm().getId(), + pus.getUser().getId(), pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(), pus.isRememberMe(), pus.getBrokerSessionId(), pus.getBrokerUserId(), offline); for (Map.Entry entry : pus.getAuthenticatedClientSessions().entrySet()) { @@ -555,7 +560,7 @@ public class MapUserSessionProvider implements UserSessionProvider { clientSession.setTimestamp(userSessionEntity.getLastSessionRefresh()); clientSession = clientSessionTx.create(clientSession); - userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSession.getId()); + userSessionEntity.setAuthenticatedClientSession(entry.getKey(), clientSession.getId()); } return userSessionEntity; @@ -581,7 +586,7 @@ public class MapUserSessionProvider implements UserSessionProvider { // check if it's an offline user session MapUserSessionEntity userSessionEntity = userSessionTx.read(withCriteria(mcb)).findFirst().orElse(null); if (userSessionEntity != null) { - if (userSessionEntity.isOffline()) { + if (Boolean.TRUE.equals(userSessionEntity.isOffline())) { return Stream.of(userSessionEntity); } } else { @@ -619,32 +624,23 @@ public class MapUserSessionProvider implements UserSessionProvider { } private MapUserSessionEntity createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) { - MapUserSessionEntity entity = new MapUserSessionEntity(null, userSession.getRealm().getId()); + MapUserSessionEntity entity = createUserSessionEntityInstance(null, userSession.getRealm().getId(), userSession.getUser().getId(), + userSession.getLoginUsername(), userSession.getIpAddress(), userSession.getAuthMethod(), userSession.isRememberMe(), + userSession.getBrokerSessionId(), userSession.getBrokerUserId(), offline); - entity.setAuthMethod(userSession.getAuthMethod()); - entity.setBrokerSessionId(userSession.getBrokerSessionId()); - entity.setBrokerUserId(userSession.getBrokerUserId()); - entity.setIpAddress(userSession.getIpAddress()); entity.setNotes(new ConcurrentHashMap<>(userSession.getNotes())); - entity.addNote(CORRESPONDING_SESSION_ID, userSession.getId()); - - entity.clearAuthenticatedClientSessions(); - entity.setRememberMe(userSession.isRememberMe()); + entity.setNote(CORRESPONDING_SESSION_ID, userSession.getId()); entity.setState(userSession.getState()); - entity.setLoginUsername(userSession.getLoginUsername()); - entity.setUserId(userSession.getUser().getId()); - entity.setStarted(userSession.getStarted()); entity.setLastSessionRefresh(userSession.getLastSessionRefresh()); - entity.setOffline(offline); return entity; } private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession, UserSessionModel userSession, boolean offline) { - MapAuthenticatedClientSessionEntity entity = new MapAuthenticatedClientSessionEntity(null, - userSession.getId(), clientSession.getRealm().getId(), clientSession.getClient().getId(), offline); + MapAuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionEntityInstance(null, userSession.getId(), + clientSession.getRealm().getId(), clientSession.getClient().getId(), offline); entity.setAction(clientSession.getAction()); entity.setAuthMethod(clientSession.getProtocol()); @@ -655,4 +651,36 @@ public class MapUserSessionProvider implements UserSessionProvider { return entity; } + + private MapUserSessionEntity createUserSessionEntityInstance(String id, String realmId, String userId, String loginUsername, String ipAddress, + String authMethod, boolean rememberMe, String brokerSessionId, String brokerUserId, + boolean offline) { + MapUserSessionEntityImpl userSessionEntity = new MapUserSessionEntityImpl(); + userSessionEntity.setId(id); + userSessionEntity.setRealmId(realmId); + userSessionEntity.setUserId(userId); + userSessionEntity.setLoginUsername(loginUsername); + userSessionEntity.setIpAddress(ipAddress); + userSessionEntity.setAuthMethod(authMethod); + userSessionEntity.setRememberMe(rememberMe); + userSessionEntity.setBrokerSessionId(brokerSessionId); + userSessionEntity.setBrokerUserId(brokerUserId); + userSessionEntity.setOffline(offline); + userSessionEntity.setStarted(Time.currentTime()); + userSessionEntity.setLastSessionRefresh(userSessionEntity.getStarted()); + return userSessionEntity; + } + + private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionEntityInstance(String id, String userSessionId, String realmId, + String clientId, boolean offline) { + MapAuthenticatedClientSessionEntityImpl clientSessionEntity = new MapAuthenticatedClientSessionEntityImpl(); + clientSessionEntity.setId(id); + clientSessionEntity.setUserSessionId(userSessionId); + clientSessionEntity.setRealmId(realmId); + clientSessionEntity.setClientId(clientId); + clientSessionEntity.setOffline(offline); + clientSessionEntity.setTimestamp(Time.currentTime()); + return clientSessionEntity; + } + } diff --git a/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java b/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java index 5da92eb6f5..ac7f1ed367 100644 --- a/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java +++ b/model/map/src/main/java/org/keycloak/models/map/userSession/SessionExpiration.java @@ -27,10 +27,11 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes; public class SessionExpiration { public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) { - if (entity.isOffline()) { - long sessionExpires = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout(); + long timestamp = entity.getTimestamp() != null ? entity.getTimestamp() : 0l; + if (Boolean.TRUE.equals(entity.isOffline())) { + long sessionExpires = timestamp + realm.getOfflineSessionIdleTimeout(); if (realm.isOfflineSessionMaxLifespanEnabled()) { - sessionExpires = entity.getTimestamp() + realm.getOfflineSessionMaxLifespan(); + sessionExpires = timestamp + realm.getOfflineSessionMaxLifespan(); long clientOfflineSessionMaxLifespan; String clientOfflineSessionMaxLifespanPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN); @@ -41,12 +42,12 @@ public class SessionExpiration { } if (clientOfflineSessionMaxLifespan > 0) { - long clientOfflineSessionMaxExpiration = entity.getTimestamp() + clientOfflineSessionMaxLifespan; + long clientOfflineSessionMaxExpiration = timestamp + clientOfflineSessionMaxLifespan; sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration); } } - long expiration = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout(); + long expiration = timestamp + realm.getOfflineSessionIdleTimeout(); long clientOfflineSessionIdleTimeout; String clientOfflineSessionIdleTimeoutPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_IDLE_TIMEOUT); @@ -57,13 +58,13 @@ public class SessionExpiration { } if (clientOfflineSessionIdleTimeout > 0) { - long clientOfflineSessionIdleExpiration = entity.getTimestamp() + clientOfflineSessionIdleTimeout; + long clientOfflineSessionIdleExpiration = timestamp + clientOfflineSessionIdleTimeout; expiration = Math.min(expiration, clientOfflineSessionIdleExpiration); } entity.setExpiration(Math.min(expiration, sessionExpires)); } else { - long sessionExpires = (long) entity.getTimestamp() + (realm.getSsoSessionMaxLifespanRememberMe() > 0 + long sessionExpires = timestamp + (realm.getSsoSessionMaxLifespanRememberMe() > 0 ? realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan()); long clientSessionMaxLifespan; @@ -75,11 +76,11 @@ public class SessionExpiration { } if (clientSessionMaxLifespan > 0) { - long clientSessionMaxExpiration = entity.getTimestamp() + clientSessionMaxLifespan; + long clientSessionMaxExpiration = timestamp + clientSessionMaxLifespan; sessionExpires = Math.min(sessionExpires, clientSessionMaxExpiration); } - long expiration = (long) entity.getTimestamp() + (realm.getSsoSessionIdleTimeoutRememberMe() > 0 + long expiration = timestamp + (realm.getSsoSessionIdleTimeoutRememberMe() > 0 ? realm.getSsoSessionIdleTimeoutRememberMe() : realm.getSsoSessionIdleTimeout()); long clientSessionIdleTimeout; @@ -91,7 +92,7 @@ public class SessionExpiration { } if (clientSessionIdleTimeout > 0) { - long clientSessionIdleExpiration = entity.getTimestamp() + clientSessionIdleTimeout; + long clientSessionIdleExpiration = timestamp + clientSessionIdleTimeout; expiration = Math.min(expiration, clientSessionIdleExpiration); } @@ -100,20 +101,22 @@ public class SessionExpiration { } public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) { - if (entity.isOffline()) { - long sessionExpires = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout(); + int started = entity.getStarted() != null ? entity.getStarted() : 0; + long lastSessionRefresh = entity.getLastSessionRefresh() != null ? entity.getLastSessionRefresh() : 0l; + if (Boolean.TRUE.equals(entity.isOffline())) { + long sessionExpires = lastSessionRefresh + realm.getOfflineSessionIdleTimeout(); if (realm.isOfflineSessionMaxLifespanEnabled()) { - sessionExpires = entity.getStarted() + realm.getOfflineSessionMaxLifespan(); + sessionExpires = started + realm.getOfflineSessionMaxLifespan(); long clientOfflineSessionMaxLifespan = realm.getClientOfflineSessionMaxLifespan(); if (clientOfflineSessionMaxLifespan > 0) { - long clientOfflineSessionMaxExpiration = entity.getStarted() + clientOfflineSessionMaxLifespan; + long clientOfflineSessionMaxExpiration = started + clientOfflineSessionMaxLifespan; sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration); } } - long expiration = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout(); + long expiration = lastSessionRefresh + realm.getOfflineSessionIdleTimeout(); long clientOfflineSessionIdleTimeout = realm.getClientOfflineSessionIdleTimeout(); @@ -124,26 +127,26 @@ public class SessionExpiration { entity.setExpiration(Math.min(expiration, sessionExpires)); } else { - long sessionExpires = (long) entity.getStarted() - + (entity.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0 + long sessionExpires = (long) started + + (Boolean.TRUE.equals(entity.isRememberMe()) && realm.getSsoSessionMaxLifespanRememberMe() > 0 ? realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan()); long clientSessionMaxLifespan = realm.getClientSessionMaxLifespan(); if (clientSessionMaxLifespan > 0) { - long clientSessionMaxExpiration = entity.getStarted() + clientSessionMaxLifespan; + long clientSessionMaxExpiration = started + clientSessionMaxLifespan; sessionExpires = Math.min(sessionExpires, clientSessionMaxExpiration); } - long expiration = (long) entity.getLastSessionRefresh() + (entity.isRememberMe() && realm.getSsoSessionIdleTimeoutRememberMe() > 0 + long expiration = lastSessionRefresh + (Boolean.TRUE.equals(entity.isRememberMe()) && realm.getSsoSessionIdleTimeoutRememberMe() > 0 ? realm.getSsoSessionIdleTimeoutRememberMe() : realm.getSsoSessionIdleTimeout()); long clientSessionIdleTimeout = realm.getClientSessionIdleTimeout(); if (clientSessionIdleTimeout > 0) { - long clientSessionIdleExpiration = entity.getLastSessionRefresh() + clientSessionIdleTimeout; + long clientSessionIdleExpiration = lastSessionRefresh + clientSessionIdleTimeout; expiration = Math.min(expiration, clientSessionIdleExpiration); }