Convert user / client session entities into interface

This commit is contained in:
Martin Kanis 2022-03-23 11:00:39 +01:00 committed by Hynek Mlnařík
parent 9cb38087b4
commit 3bb4081bd1
9 changed files with 222 additions and 461 deletions

View file

@ -56,11 +56,6 @@ public interface MapUserLoginFailureEntity extends AbstractEntity, UpdatableEnti
setLastFailure(null); setLastFailure(null);
setLastIPFailure(null); setLastIPFailure(null);
} }
@Override
public String toString() {
return String.format("%s@%08x", getId(), hashCode());
}
} }
String getRealmId(); String getRealmId();

View file

@ -67,6 +67,10 @@ import org.keycloak.models.map.user.MapUserCredentialEntityImpl;
import org.keycloak.models.map.user.MapUserEntityImpl; import org.keycloak.models.map.user.MapUserEntityImpl;
import org.keycloak.models.map.user.MapUserFederatedIdentityEntityImpl; import org.keycloak.models.map.user.MapUserFederatedIdentityEntityImpl;
import org.keycloak.models.map.storage.ModelEntityUtil; 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.EnvironmentDependentProviderFactory;
import org.keycloak.provider.ProviderConfigProperty; import org.keycloak.provider.ProviderConfigProperty;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
@ -131,6 +135,8 @@ public class ConcurrentHashMapStorageProviderFactory implements AmphibianProvide
.constructor(MapRootAuthenticationSessionEntity.class, MapRootAuthenticationSessionEntityImpl::new) .constructor(MapRootAuthenticationSessionEntity.class, MapRootAuthenticationSessionEntityImpl::new)
.constructor(MapAuthenticationSessionEntity.class, MapAuthenticationSessionEntityImpl::new) .constructor(MapAuthenticationSessionEntity.class, MapAuthenticationSessionEntityImpl::new)
.constructor(MapUserLoginFailureEntity.class, MapUserLoginFailureEntityImpl::new) .constructor(MapUserLoginFailureEntity.class, MapUserLoginFailureEntityImpl::new)
.constructor(MapUserSessionEntity.class, MapUserSessionEntityImpl::new)
.constructor(MapAuthenticatedClientSessionEntity.class, MapAuthenticatedClientSessionEntityImpl::new)
.build(); .build();
private static final Map<String, StringKeyConverter> KEY_CONVERTERS = new HashMap<>(); private static final Map<String, StringKeyConverter> KEY_CONVERTERS = new HashMap<>();

View file

@ -491,7 +491,7 @@ public class MapFieldPredicates {
private static MapModelCriteriaBuilder<Object, MapUserSessionEntity, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder<Object, MapUserSessionEntity, UserSessionModel> mcb, Operator op, Object[] values) { private static MapModelCriteriaBuilder<Object, MapUserSessionEntity, UserSessionModel> checkUserSessionContainsAuthenticatedClientSession(MapModelCriteriaBuilder<Object, MapUserSessionEntity, UserSessionModel> mcb, Operator op, Object[] values) {
String clientId = ensureEqSingleValue(UserSessionModel.SearchableFields.CLIENT_ID, "client_id", op, values); String clientId = ensureEqSingleValue(UserSessionModel.SearchableFields.CLIENT_ID, "client_id", op, values);
Function<MapUserSessionEntity, ?> getter = use -> (use.getAuthenticatedClientSessions().containsKey(clientId)); Function<MapUserSessionEntity, ?> getter = use -> (use.getAuthenticatedClientSession(clientId) != null);
return mcb.fieldCompare(Boolean.TRUE::equals, getter); return mcb.fieldCompare(Boolean.TRUE::equals, getter);
} }

View file

@ -21,6 +21,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel; import org.keycloak.models.UserSessionModel;
import java.util.Collections;
import java.util.Map; import java.util.Map;
/** /**
@ -40,7 +41,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
@Override @Override
public int getTimestamp() { public int getTimestamp() {
return entity.getTimestamp(); Integer timestamp = entity.getTimestamp();
return timestamp != null ? timestamp : 0;
} }
@Override @Override
@ -65,7 +67,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
@Override @Override
public int getCurrentRefreshTokenUseCount() { public int getCurrentRefreshTokenUseCount() {
return entity.getCurrentRefreshTokenUseCount(); Integer currentRefreshTokenUseCount = entity.getCurrentRefreshTokenUseCount();
return currentRefreshTokenUseCount != null ? currentRefreshTokenUseCount : 0;
} }
@Override @Override
@ -75,7 +78,7 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
@Override @Override
public String getNote(String name) { public String getNote(String name) {
return (name != null) ? entity.getNotes().get(name) : null; return (name != null) ? entity.getNote(name) : null;
} }
@Override @Override
@ -84,7 +87,7 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
if (value == null) { if (value == null) {
entity.removeNote(name); entity.removeNote(name);
} else { } else {
entity.addNote(name, value); entity.setNote(name, value);
} }
} }
} }
@ -98,7 +101,8 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
@Override @Override
public Map<String, String> getNotes() { public Map<String, String> getNotes() {
return entity.getNotes(); Map<String, String> notes = entity.getNotes();
return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes);
} }
@Override @Override

View file

@ -16,51 +16,25 @@
*/ */
package org.keycloak.models.map.userSession; 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.AbstractEntity;
import org.keycloak.models.map.common.DeepCloner;
import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.map.common.UpdatableEntity;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a> * @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/ */
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 {
public abstract class AbstractAuthenticatedClientSessionEntity extends UpdatableEntity.Impl implements MapAuthenticatedClientSessionEntity {
private String id; private String id;
private String userSessionId;
private String realmId;
private String clientId;
/**
* Flag signalizing that any of the setters has been meaningfully used.
*/
private String authMethod;
private String redirectUri;
private volatile int timestamp;
private long expiration;
private String action;
private Map<String, String> 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 @Override
public String getId() { public String getId() {
@ -73,127 +47,44 @@ public class MapAuthenticatedClientSessionEntity extends UpdatableEntity.Impl im
this.id = id; this.id = id;
this.updated |= id != null; this.updated |= id != null;
} }
public String getRealmId() {
return realmId;
} }
public void setRealmId(String realmId) { String getRealmId();
this.updated |= !Objects.equals(this.realmId, realmId); void setRealmId(String realmId);
this.realmId = realmId;
}
public String getClientId() { String getClientId();
return clientId; void setClientId(String clientId);
}
public void setClientId(String clientId) { String getUserSessionId();
this.updated |= !Objects.equals(this.clientId, clientId); void setUserSessionId(String userSessionId);
this.clientId = clientId;
}
public String getUserSessionId() { String getAuthMethod();
return userSessionId; void setAuthMethod(String authMethod);
}
public void setUserSessionId(String userSessionId) { String getRedirectUri();
this.updated |= !Objects.equals(this.userSessionId, userSessionId); void setRedirectUri(String redirectUri);
this.userSessionId = userSessionId;
}
public String getAuthMethod() { Integer getTimestamp();
return authMethod; void setTimestamp(Integer timestamp);
}
public void setAuthMethod(String authMethod) { Long getExpiration();
this.updated |= !Objects.equals(this.authMethod, authMethod); void setExpiration(Long expiration);
this.authMethod = authMethod;
}
public String getRedirectUri() { String getAction();
return redirectUri; void setAction(String action);
}
public void setRedirectUri(String redirectUri) { Map<String, String> getNotes();
this.updated |= !Objects.equals(this.redirectUri, redirectUri); void setNotes(Map<String, String> notes);
this.redirectUri = redirectUri; String getNote(String name);
} Boolean removeNote(String name);
void setNote(String name, String value);
public int getTimestamp() { String getCurrentRefreshToken();
return timestamp; void setCurrentRefreshToken(String currentRefreshToken);
}
public void setTimestamp(int timestamp) { Integer getCurrentRefreshTokenUseCount();
this.updated |= this.timestamp != timestamp; void setCurrentRefreshTokenUseCount(Integer currentRefreshTokenUseCount);
this.timestamp = timestamp;
}
public long getExpiration() { Boolean isOffline();
return expiration; void setOffline(Boolean offline);
}
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<String, String> getNotes() {
return notes;
}
public void setNotes(Map<String, String> 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());
}
} }

View file

@ -83,17 +83,20 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override @Override
public boolean isRememberMe() { public boolean isRememberMe() {
return entity.isRememberMe(); Boolean rememberMe = entity.isRememberMe();
return rememberMe != null ? rememberMe : false;
} }
@Override @Override
public int getStarted() { public int getStarted() {
return entity.getStarted(); Integer started = entity.getStarted();
return started != null ? started : 0;
} }
@Override @Override
public int getLastSessionRefresh() { public int getLastSessionRefresh() {
return entity.getLastSessionRefresh(); Integer lastSessionRefresh = entity.getLastSessionRefresh();
return lastSessionRefresh != null ? lastSessionRefresh : 0;
} }
@Override @Override
@ -103,7 +106,8 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override @Override
public boolean isOffline() { public boolean isOffline() {
return entity.isOffline(); Boolean offline = entity.isOffline();
return offline != null ? offline : false;
} }
@Override @Override
@ -111,8 +115,13 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
Map<String, AuthenticatedClientSessionModel> result = new HashMap<>(); Map<String, AuthenticatedClientSessionModel> result = new HashMap<>();
List<String> removedClientUUIDS = new LinkedList<>(); List<String> removedClientUUIDS = new LinkedList<>();
Map<String, String> authenticatedClientSessions = entity.getAuthenticatedClientSessions();
if (authenticatedClientSessions == null) {
return Collections.emptyMap();
} else {
// to avoid concurrentModificationException // to avoid concurrentModificationException
Map<String, String> authenticatedClientSessions = new HashMap<>(entity.getAuthenticatedClientSessions()); authenticatedClientSessions = new HashMap<>(authenticatedClientSessions);
}
authenticatedClientSessions.forEach((clientUUID, clientSessionId) -> { authenticatedClientSessions.forEach((clientUUID, clientSessionId) -> {
ClientModel client = realm.getClientById(clientUUID); ClientModel client = realm.getClientById(clientUUID);
@ -135,7 +144,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override @Override
public AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) { public AuthenticatedClientSessionModel getAuthenticatedClientSessionByClient(String clientUUID) {
String clientSessionId = entity.getAuthenticatedClientSessions().get(clientUUID); String clientSessionId = entity.getAuthenticatedClientSession(clientUUID);
if (clientSessionId == null) { if (clientSessionId == null) {
return null; return null;
@ -155,7 +164,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override @Override
public String getNote(String name) { public String getNote(String name) {
return (name != null) ? entity.getNotes().get(name) : null; return (name != null) ? entity.getNote(name) : null;
} }
@Override @Override
@ -164,7 +173,7 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
if (value == null) { if (value == null) {
entity.removeNote(name); entity.removeNote(name);
} else { } else {
entity.addNote(name, value); entity.setNote(name, value);
} }
} }
} }
@ -178,7 +187,8 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override @Override
public Map<String, String> getNotes() { public Map<String, String> getNotes() {
return entity.getNotes(); Map<String, String> notes = entity.getNotes();
return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes);
} }
@Override @Override
@ -212,9 +222,9 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
String correspondingSessionId = entity.getNote(CORRESPONDING_SESSION_ID); String correspondingSessionId = entity.getNote(CORRESPONDING_SESSION_ID);
entity.setNotes(new ConcurrentHashMap<>()); entity.setNotes(new ConcurrentHashMap<>());
if (correspondingSessionId != null) if (correspondingSessionId != null)
entity.addNote(CORRESPONDING_SESSION_ID, correspondingSessionId); entity.setNote(CORRESPONDING_SESSION_ID, correspondingSessionId);
entity.clearAuthenticatedClientSessions(); entity.setAuthenticatedClientSessions(null);
} }
@Override @Override

View file

@ -16,82 +16,28 @@
*/ */
package org.keycloak.models.map.userSession; 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.UserSessionModel;
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
import org.keycloak.models.map.common.AbstractEntity; import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.common.DeepCloner;
import org.keycloak.models.map.common.UpdatableEntity; import org.keycloak.models.map.common.UpdatableEntity;
import java.util.Map; import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
/** /**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a> * @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
*/ */
public class MapUserSessionEntity extends UpdatableEntity.Impl implements AbstractEntity { @GenerateEntityImplementations(
inherits = "org.keycloak.models.map.userSession.MapUserSessionEntity.AbstractUserSessionEntity"
)
@DeepCloner.Root
public interface MapUserSessionEntity extends AbstractEntity, UpdatableEntity {
public abstract class AbstractUserSessionEntity extends UpdatableEntity.Impl implements MapUserSessionEntity {
private String id; private String id;
private String realmId;
/**
* Flag signalizing that any of the setters has been meaningfully used.
*/
private String userId;
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<String, String> notes = new ConcurrentHashMap<>();
private UserSessionModel.State state;
private UserSessionModel.SessionPersistenceState persistenceState = UserSessionModel.SessionPersistenceState.PERSISTENT;
private Map<String, String> authenticatedClientSessions = new ConcurrentHashMap<>();
private boolean offline;
public MapUserSessionEntity() {}
public MapUserSessionEntity(String id, String realmId) {
this.id = id;
this.realmId = realmId;
}
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;
}
@Override @Override
public String getId() { public String getId() {
return this.id; return this.id;
@ -103,182 +49,60 @@ public class MapUserSessionEntity extends UpdatableEntity.Impl implements Abstra
this.id = id; this.id = id;
this.updated |= id != null; this.updated |= id != null;
} }
public String getRealmId() {
return realmId;
} }
public void setRealmId(String realmId) { String getRealmId();
this.updated |= !Objects.equals(this.realmId, realmId); void setRealmId(String realmId);
this.realmId = realmId;
}
public String getUserId() { String getUserId();
return userId; void setUserId(String userId);
}
public void setUserId(String userId) { String getBrokerSessionId();
this.updated |= !Objects.equals(this.userId, userId); void setBrokerSessionId(String brokerSessionId);
this.userId = userId;
}
public String getBrokerSessionId() { String getBrokerUserId();
return brokerSessionId; void setBrokerUserId(String brokerUserId);
}
public void setBrokerSessionId(String brokerSessionId) { String getLoginUsername();
this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId); void setLoginUsername(String loginUsername);
this.brokerSessionId = brokerSessionId;
}
public String getBrokerUserId() { String getIpAddress();
return brokerUserId; void setIpAddress(String ipAddress);
}
public void setBrokerUserId(String brokerUserId) { String getAuthMethod();
this.updated |= !Objects.equals(this.brokerUserId, brokerUserId); void setAuthMethod(String authMethod);
this.brokerUserId = brokerUserId;
}
public String getLoginUsername() { Boolean isRememberMe();
return loginUsername; void setRememberMe(Boolean rememberMe);
}
public void setLoginUsername(String loginUsername) { Integer getStarted();
this.updated |= !Objects.equals(this.loginUsername, loginUsername); void setStarted(Integer started);
this.loginUsername = loginUsername;
}
public String getIpAddress() { Integer getLastSessionRefresh();
return ipAddress;
}
public void setIpAddress(String ipAddress) { void setLastSessionRefresh(Integer lastSessionRefresh);
this.updated |= !Objects.equals(this.ipAddress, ipAddress);
this.ipAddress = ipAddress;
}
public String getAuthMethod() { Long getExpiration();
return authMethod; void setExpiration(Long expiration);
}
public void setAuthMethod(String authMethod) { Map<String, String> getNotes();
this.updated |= !Objects.equals(this.authMethod, authMethod); String getNote(String name);
this.authMethod = authMethod; void setNotes(Map<String, String> notes);
} Boolean removeNote(String name);
void setNote(String name, String value);
public boolean isRememberMe() { UserSessionModel.State getState();
return rememberMe; void setState(UserSessionModel.State state);
}
public void setRememberMe(boolean rememberMe) { Map<String, String> getAuthenticatedClientSessions();
this.updated |= this.rememberMe != rememberMe; void setAuthenticatedClientSessions(Map<String, String> authenticatedClientSessions);
this.rememberMe = rememberMe; String getAuthenticatedClientSession(String clientUUID);
} void setAuthenticatedClientSession(String clientUUID, String clientSessionId);
Boolean removeAuthenticatedClientSession(String clientUUID);
public int getStarted() { Boolean isOffline();
return started; void setOffline(Boolean offline);
}
public void setStarted(int started) { UserSessionModel.SessionPersistenceState getPersistenceState();
this.updated |= this.started != started; void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState);
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<String, String> getNotes() {
return notes;
}
public String getNote(String name) {
return notes.get(name);
}
public void setNotes(Map<String, String> 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<String, String> getAuthenticatedClientSessions() {
return authenticatedClientSessions;
}
public void setAuthenticatedClientSessions(Map<String, String> 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());
}
} }

View file

@ -81,7 +81,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
private Function<MapUserSessionEntity, UserSessionModel> userEntityToAdapterFunc(RealmModel realm) { private Function<MapUserSessionEntity, UserSessionModel> userEntityToAdapterFunc(RealmModel realm) {
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller // Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return (origEntity) -> { 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)) { if (Objects.equals(origEntity.getPersistenceState(), TRANSIENT)) {
transientUserSessions.remove(origEntity.getId()); transientUserSessions.remove(origEntity.getId());
} }
@ -110,7 +111,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
UserSessionModel userSession) { UserSessionModel userSession) {
// Clone entity before returning back, to avoid giving away a reference to the live object to the caller // Clone entity before returning back, to avoid giving away a reference to the live object to the caller
return origEntity -> { 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())); userSession.removeAuthenticatedClientSessions(Arrays.asList(origEntity.getClientId()));
clientSessionTx.delete(origEntity.getId()); clientSessionTx.delete(origEntity.getId());
return null; return null;
@ -141,9 +143,10 @@ public class MapUserSessionProvider implements UserSessionProvider {
@Override @Override
public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) { public AuthenticatedClientSessionModel createClientSession(RealmModel realm, ClientModel client, UserSessionModel userSession) {
MapAuthenticatedClientSessionEntity entity = MapAuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionEntityInstance(null, userSession.getId(),
new MapAuthenticatedClientSessionEntity(null, userSession.getId(), realm.getId(), client.getId(), false); realm.getId(), client.getId(), false);
entity.getNotes().put(AuthenticatedClientSessionModel.STARTED_AT_NOTE, String.valueOf(entity.getTimestamp())); String started = entity.getTimestamp() != null ? String.valueOf(entity.getTimestamp()) : String.valueOf(0);
entity.setNote(AuthenticatedClientSessionModel.STARTED_AT_NOTE, started);
setClientSessionExpiration(entity, realm, client); setClientSessionExpiration(entity, realm, client);
LOG.tracef("createClientSession(%s, %s, %s)%s", realm, client, userSession, getShortStackTrace()); 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()); 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); return clientEntityToAdapterFunc(realm, client, userSession).apply(entity);
} }
@ -204,13 +207,15 @@ public class MapUserSessionProvider implements UserSessionProvider {
if (id == null) { if (id == null) {
id = UUID.randomUUID().toString(); 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); transientUserSessions.put(entity.getId(), entity);
} else { } else {
if (id != null && userSessionTx.read(id) != null) { if (id != null && userSessionTx.read(id) != null) {
throw new ModelDuplicateException("User session exists: " + id); 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); entity = userSessionTx.create(entity);
} }
@ -461,14 +466,14 @@ public class MapUserSessionProvider implements UserSessionProvider {
MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true); MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
int currentTime = Time.currentTime(); 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); clientSessionEntity.setTimestamp(currentTime);
setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient()); setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient());
clientSessionEntity = clientSessionTx.create(clientSessionEntity); clientSessionEntity = clientSessionTx.create(clientSessionEntity);
Optional<MapUserSessionEntity> userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst(); Optional<MapUserSessionEntity> userSessionEntity = getOfflineUserSessionEntityStream(clientSession.getRealm(), offlineUserSession.getId()).findFirst();
if (userSessionEntity.isPresent()) { if (userSessionEntity.isPresent()) {
userSessionEntity.get().addAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId()); userSessionEntity.get().setAuthenticatedClientSession(clientSession.getClient().getId(), clientSessionEntity.getId());
} }
return clientEntityToAdapterFunc(clientSession.getRealm(), return clientEntityToAdapterFunc(clientSession.getRealm(),
@ -544,8 +549,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
persistentUserSessions.stream() persistentUserSessions.stream()
.map(pus -> { .map(pus -> {
MapUserSessionEntity userSessionEntity = new MapUserSessionEntity(null, pus.getRealm(), pus.getUser(), MapUserSessionEntity userSessionEntity = createUserSessionEntityInstance(null, pus.getRealm().getId(),
pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(), pus.getUser().getId(), pus.getLoginUsername(), pus.getIpAddress(), pus.getAuthMethod(),
pus.isRememberMe(), pus.getBrokerSessionId(), pus.getBrokerUserId(), offline); pus.isRememberMe(), pus.getBrokerSessionId(), pus.getBrokerUserId(), offline);
for (Map.Entry<String, AuthenticatedClientSessionModel> entry : pus.getAuthenticatedClientSessions().entrySet()) { for (Map.Entry<String, AuthenticatedClientSessionModel> entry : pus.getAuthenticatedClientSessions().entrySet()) {
@ -555,7 +560,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
clientSession.setTimestamp(userSessionEntity.getLastSessionRefresh()); clientSession.setTimestamp(userSessionEntity.getLastSessionRefresh());
clientSession = clientSessionTx.create(clientSession); clientSession = clientSessionTx.create(clientSession);
userSessionEntity.addAuthenticatedClientSession(entry.getKey(), clientSession.getId()); userSessionEntity.setAuthenticatedClientSession(entry.getKey(), clientSession.getId());
} }
return userSessionEntity; return userSessionEntity;
@ -581,7 +586,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
// check if it's an offline user session // check if it's an offline user session
MapUserSessionEntity userSessionEntity = userSessionTx.read(withCriteria(mcb)).findFirst().orElse(null); MapUserSessionEntity userSessionEntity = userSessionTx.read(withCriteria(mcb)).findFirst().orElse(null);
if (userSessionEntity != null) { if (userSessionEntity != null) {
if (userSessionEntity.isOffline()) { if (Boolean.TRUE.equals(userSessionEntity.isOffline())) {
return Stream.of(userSessionEntity); return Stream.of(userSessionEntity);
} }
} else { } else {
@ -619,32 +624,23 @@ public class MapUserSessionProvider implements UserSessionProvider {
} }
private MapUserSessionEntity createUserSessionEntityInstance(UserSessionModel userSession, boolean offline) { 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.setNotes(new ConcurrentHashMap<>(userSession.getNotes()));
entity.addNote(CORRESPONDING_SESSION_ID, userSession.getId()); entity.setNote(CORRESPONDING_SESSION_ID, userSession.getId());
entity.clearAuthenticatedClientSessions();
entity.setRememberMe(userSession.isRememberMe());
entity.setState(userSession.getState()); entity.setState(userSession.getState());
entity.setLoginUsername(userSession.getLoginUsername());
entity.setUserId(userSession.getUser().getId());
entity.setStarted(userSession.getStarted()); entity.setStarted(userSession.getStarted());
entity.setLastSessionRefresh(userSession.getLastSessionRefresh()); entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
entity.setOffline(offline);
return entity; return entity;
} }
private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession, private MapAuthenticatedClientSessionEntity createAuthenticatedClientSessionInstance(AuthenticatedClientSessionModel clientSession,
UserSessionModel userSession, boolean offline) { UserSessionModel userSession, boolean offline) {
MapAuthenticatedClientSessionEntity entity = new MapAuthenticatedClientSessionEntity(null, MapAuthenticatedClientSessionEntity entity = createAuthenticatedClientSessionEntityInstance(null, userSession.getId(),
userSession.getId(), clientSession.getRealm().getId(), clientSession.getClient().getId(), offline); clientSession.getRealm().getId(), clientSession.getClient().getId(), offline);
entity.setAction(clientSession.getAction()); entity.setAction(clientSession.getAction());
entity.setAuthMethod(clientSession.getProtocol()); entity.setAuthMethod(clientSession.getProtocol());
@ -655,4 +651,36 @@ public class MapUserSessionProvider implements UserSessionProvider {
return entity; 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;
}
} }

View file

@ -27,10 +27,11 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes;
public class SessionExpiration { public class SessionExpiration {
public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) { public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) {
if (entity.isOffline()) { long timestamp = entity.getTimestamp() != null ? entity.getTimestamp() : 0l;
long sessionExpires = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout(); if (Boolean.TRUE.equals(entity.isOffline())) {
long sessionExpires = timestamp + realm.getOfflineSessionIdleTimeout();
if (realm.isOfflineSessionMaxLifespanEnabled()) { if (realm.isOfflineSessionMaxLifespanEnabled()) {
sessionExpires = entity.getTimestamp() + realm.getOfflineSessionMaxLifespan(); sessionExpires = timestamp + realm.getOfflineSessionMaxLifespan();
long clientOfflineSessionMaxLifespan; long clientOfflineSessionMaxLifespan;
String clientOfflineSessionMaxLifespanPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN); String clientOfflineSessionMaxLifespanPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_MAX_LIFESPAN);
@ -41,12 +42,12 @@ public class SessionExpiration {
} }
if (clientOfflineSessionMaxLifespan > 0) { if (clientOfflineSessionMaxLifespan > 0) {
long clientOfflineSessionMaxExpiration = entity.getTimestamp() + clientOfflineSessionMaxLifespan; long clientOfflineSessionMaxExpiration = timestamp + clientOfflineSessionMaxLifespan;
sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration); sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration);
} }
} }
long expiration = entity.getTimestamp() + realm.getOfflineSessionIdleTimeout(); long expiration = timestamp + realm.getOfflineSessionIdleTimeout();
long clientOfflineSessionIdleTimeout; long clientOfflineSessionIdleTimeout;
String clientOfflineSessionIdleTimeoutPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_IDLE_TIMEOUT); String clientOfflineSessionIdleTimeoutPerClient = client.getAttribute(OIDCConfigAttributes.CLIENT_OFFLINE_SESSION_IDLE_TIMEOUT);
@ -57,13 +58,13 @@ public class SessionExpiration {
} }
if (clientOfflineSessionIdleTimeout > 0) { if (clientOfflineSessionIdleTimeout > 0) {
long clientOfflineSessionIdleExpiration = entity.getTimestamp() + clientOfflineSessionIdleTimeout; long clientOfflineSessionIdleExpiration = timestamp + clientOfflineSessionIdleTimeout;
expiration = Math.min(expiration, clientOfflineSessionIdleExpiration); expiration = Math.min(expiration, clientOfflineSessionIdleExpiration);
} }
entity.setExpiration(Math.min(expiration, sessionExpires)); entity.setExpiration(Math.min(expiration, sessionExpires));
} else { } else {
long sessionExpires = (long) entity.getTimestamp() + (realm.getSsoSessionMaxLifespanRememberMe() > 0 long sessionExpires = timestamp + (realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan()); ? realm.getSsoSessionMaxLifespanRememberMe() : realm.getSsoSessionMaxLifespan());
long clientSessionMaxLifespan; long clientSessionMaxLifespan;
@ -75,11 +76,11 @@ public class SessionExpiration {
} }
if (clientSessionMaxLifespan > 0) { if (clientSessionMaxLifespan > 0) {
long clientSessionMaxExpiration = entity.getTimestamp() + clientSessionMaxLifespan; long clientSessionMaxExpiration = timestamp + clientSessionMaxLifespan;
sessionExpires = Math.min(sessionExpires, clientSessionMaxExpiration); sessionExpires = Math.min(sessionExpires, clientSessionMaxExpiration);
} }
long expiration = (long) entity.getTimestamp() + (realm.getSsoSessionIdleTimeoutRememberMe() > 0 long expiration = timestamp + (realm.getSsoSessionIdleTimeoutRememberMe() > 0
? realm.getSsoSessionIdleTimeoutRememberMe() : realm.getSsoSessionIdleTimeout()); ? realm.getSsoSessionIdleTimeoutRememberMe() : realm.getSsoSessionIdleTimeout());
long clientSessionIdleTimeout; long clientSessionIdleTimeout;
@ -91,7 +92,7 @@ public class SessionExpiration {
} }
if (clientSessionIdleTimeout > 0) { if (clientSessionIdleTimeout > 0) {
long clientSessionIdleExpiration = entity.getTimestamp() + clientSessionIdleTimeout; long clientSessionIdleExpiration = timestamp + clientSessionIdleTimeout;
expiration = Math.min(expiration, clientSessionIdleExpiration); expiration = Math.min(expiration, clientSessionIdleExpiration);
} }
@ -100,20 +101,22 @@ public class SessionExpiration {
} }
public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) { public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) {
if (entity.isOffline()) { int started = entity.getStarted() != null ? entity.getStarted() : 0;
long sessionExpires = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout(); long lastSessionRefresh = entity.getLastSessionRefresh() != null ? entity.getLastSessionRefresh() : 0l;
if (Boolean.TRUE.equals(entity.isOffline())) {
long sessionExpires = lastSessionRefresh + realm.getOfflineSessionIdleTimeout();
if (realm.isOfflineSessionMaxLifespanEnabled()) { if (realm.isOfflineSessionMaxLifespanEnabled()) {
sessionExpires = entity.getStarted() + realm.getOfflineSessionMaxLifespan(); sessionExpires = started + realm.getOfflineSessionMaxLifespan();
long clientOfflineSessionMaxLifespan = realm.getClientOfflineSessionMaxLifespan(); long clientOfflineSessionMaxLifespan = realm.getClientOfflineSessionMaxLifespan();
if (clientOfflineSessionMaxLifespan > 0) { if (clientOfflineSessionMaxLifespan > 0) {
long clientOfflineSessionMaxExpiration = entity.getStarted() + clientOfflineSessionMaxLifespan; long clientOfflineSessionMaxExpiration = started + clientOfflineSessionMaxLifespan;
sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration); sessionExpires = Math.min(sessionExpires, clientOfflineSessionMaxExpiration);
} }
} }
long expiration = entity.getLastSessionRefresh() + realm.getOfflineSessionIdleTimeout(); long expiration = lastSessionRefresh + realm.getOfflineSessionIdleTimeout();
long clientOfflineSessionIdleTimeout = realm.getClientOfflineSessionIdleTimeout(); long clientOfflineSessionIdleTimeout = realm.getClientOfflineSessionIdleTimeout();
@ -124,26 +127,26 @@ public class SessionExpiration {
entity.setExpiration(Math.min(expiration, sessionExpires)); entity.setExpiration(Math.min(expiration, sessionExpires));
} else { } else {
long sessionExpires = (long) entity.getStarted() long sessionExpires = (long) started
+ (entity.isRememberMe() && realm.getSsoSessionMaxLifespanRememberMe() > 0 + (Boolean.TRUE.equals(entity.isRememberMe()) && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe() ? realm.getSsoSessionMaxLifespanRememberMe()
: realm.getSsoSessionMaxLifespan()); : realm.getSsoSessionMaxLifespan());
long clientSessionMaxLifespan = realm.getClientSessionMaxLifespan(); long clientSessionMaxLifespan = realm.getClientSessionMaxLifespan();
if (clientSessionMaxLifespan > 0) { if (clientSessionMaxLifespan > 0) {
long clientSessionMaxExpiration = entity.getStarted() + clientSessionMaxLifespan; long clientSessionMaxExpiration = started + clientSessionMaxLifespan;
sessionExpires = Math.min(sessionExpires, clientSessionMaxExpiration); 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.getSsoSessionIdleTimeoutRememberMe()
: realm.getSsoSessionIdleTimeout()); : realm.getSsoSessionIdleTimeout());
long clientSessionIdleTimeout = realm.getClientSessionIdleTimeout(); long clientSessionIdleTimeout = realm.getClientSessionIdleTimeout();
if (clientSessionIdleTimeout > 0) { if (clientSessionIdleTimeout > 0) {
long clientSessionIdleExpiration = entity.getLastSessionRefresh() + clientSessionIdleTimeout; long clientSessionIdleExpiration = lastSessionRefresh + clientSessionIdleTimeout;
expiration = Math.min(expiration, clientSessionIdleExpiration); expiration = Math.min(expiration, clientSessionIdleExpiration);
} }