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);
setLastIPFailure(null);
}
@Override
public String toString() {
return String.format("%s@%08x", getId(), hashCode());
}
}
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.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<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) {
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);
}

View file

@ -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<String, String> getNotes() {
return entity.getNotes();
Map<String, String> notes = entity.getNotes();
return notes == null ? Collections.emptyMap() : Collections.unmodifiableMap(notes);
}
@Override

View file

@ -16,51 +16,25 @@
*/
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 <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 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
public String getId() {
@ -73,127 +47,44 @@ public class MapAuthenticatedClientSessionEntity extends UpdatableEntity.Impl im
this.id = id;
this.updated |= id != null;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.updated |= !Objects.equals(this.realmId, realmId);
this.realmId = realmId;
}
String getRealmId();
void setRealmId(String realmId);
public String getClientId() {
return clientId;
}
String getClientId();
void setClientId(String clientId);
public void setClientId(String clientId) {
this.updated |= !Objects.equals(this.clientId, clientId);
this.clientId = clientId;
}
String getUserSessionId();
void setUserSessionId(String userSessionId);
public String getUserSessionId() {
return userSessionId;
}
String getAuthMethod();
void setAuthMethod(String authMethod);
public void setUserSessionId(String userSessionId) {
this.updated |= !Objects.equals(this.userSessionId, userSessionId);
this.userSessionId = userSessionId;
}
String getRedirectUri();
void setRedirectUri(String redirectUri);
public String getAuthMethod() {
return authMethod;
}
Integer getTimestamp();
void setTimestamp(Integer timestamp);
public void setAuthMethod(String authMethod) {
this.updated |= !Objects.equals(this.authMethod, authMethod);
this.authMethod = authMethod;
}
Long getExpiration();
void setExpiration(Long expiration);
public String getRedirectUri() {
return redirectUri;
}
String getAction();
void setAction(String action);
public void setRedirectUri(String redirectUri) {
this.updated |= !Objects.equals(this.redirectUri, redirectUri);
this.redirectUri = redirectUri;
}
Map<String, String> getNotes();
void setNotes(Map<String, String> notes);
String getNote(String name);
Boolean removeNote(String name);
void setNote(String name, String value);
public int getTimestamp() {
return timestamp;
}
String getCurrentRefreshToken();
void setCurrentRefreshToken(String currentRefreshToken);
public void setTimestamp(int timestamp) {
this.updated |= this.timestamp != timestamp;
this.timestamp = timestamp;
}
Integer getCurrentRefreshTokenUseCount();
void setCurrentRefreshTokenUseCount(Integer currentRefreshTokenUseCount);
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<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());
}
Boolean isOffline();
void setOffline(Boolean offline);
}

View file

@ -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<String, AuthenticatedClientSessionModel> result = new HashMap<>();
List<String> removedClientUUIDS = new LinkedList<>();
Map<String, String> authenticatedClientSessions = entity.getAuthenticatedClientSessions();
if (authenticatedClientSessions == null) {
return Collections.emptyMap();
} else {
// to avoid concurrentModificationException
Map<String, String> authenticatedClientSessions = new HashMap<>(entity.getAuthenticatedClientSessions());
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<String, String> getNotes() {
return entity.getNotes();
Map<String, String> 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

View file

@ -16,82 +16,28 @@
*/
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 <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 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
public String getId() {
return this.id;
@ -103,182 +49,60 @@ public class MapUserSessionEntity extends UpdatableEntity.Impl implements Abstra
this.id = id;
this.updated |= id != null;
}
public String getRealmId() {
return realmId;
}
public void setRealmId(String realmId) {
this.updated |= !Objects.equals(this.realmId, realmId);
this.realmId = realmId;
}
String getRealmId();
void setRealmId(String realmId);
public String getUserId() {
return userId;
}
String getUserId();
void setUserId(String userId);
public void setUserId(String userId) {
this.updated |= !Objects.equals(this.userId, userId);
this.userId = userId;
}
String getBrokerSessionId();
void setBrokerSessionId(String brokerSessionId);
public String getBrokerSessionId() {
return brokerSessionId;
}
String getBrokerUserId();
void setBrokerUserId(String brokerUserId);
public void setBrokerSessionId(String brokerSessionId) {
this.updated |= !Objects.equals(this.brokerSessionId, brokerSessionId);
this.brokerSessionId = brokerSessionId;
}
String getLoginUsername();
void setLoginUsername(String loginUsername);
public String getBrokerUserId() {
return brokerUserId;
}
String getIpAddress();
void setIpAddress(String ipAddress);
public void setBrokerUserId(String brokerUserId) {
this.updated |= !Objects.equals(this.brokerUserId, brokerUserId);
this.brokerUserId = brokerUserId;
}
String getAuthMethod();
void setAuthMethod(String authMethod);
public String getLoginUsername() {
return loginUsername;
}
Boolean isRememberMe();
void setRememberMe(Boolean rememberMe);
public void setLoginUsername(String loginUsername) {
this.updated |= !Objects.equals(this.loginUsername, loginUsername);
this.loginUsername = loginUsername;
}
Integer getStarted();
void setStarted(Integer started);
public String getIpAddress() {
return ipAddress;
}
Integer getLastSessionRefresh();
public void setIpAddress(String ipAddress) {
this.updated |= !Objects.equals(this.ipAddress, ipAddress);
this.ipAddress = ipAddress;
}
void setLastSessionRefresh(Integer lastSessionRefresh);
public String getAuthMethod() {
return authMethod;
}
Long getExpiration();
void setExpiration(Long expiration);
public void setAuthMethod(String authMethod) {
this.updated |= !Objects.equals(this.authMethod, authMethod);
this.authMethod = authMethod;
}
Map<String, String> getNotes();
String getNote(String name);
void setNotes(Map<String, String> notes);
Boolean removeNote(String name);
void setNote(String name, String value);
public boolean isRememberMe() {
return rememberMe;
}
UserSessionModel.State getState();
void setState(UserSessionModel.State state);
public void setRememberMe(boolean rememberMe) {
this.updated |= this.rememberMe != rememberMe;
this.rememberMe = rememberMe;
}
Map<String, String> getAuthenticatedClientSessions();
void setAuthenticatedClientSessions(Map<String, String> authenticatedClientSessions);
String getAuthenticatedClientSession(String clientUUID);
void setAuthenticatedClientSession(String clientUUID, String clientSessionId);
Boolean removeAuthenticatedClientSession(String clientUUID);
public int getStarted() {
return started;
}
Boolean isOffline();
void setOffline(Boolean offline);
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<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());
}
UserSessionModel.SessionPersistenceState getPersistenceState();
void setPersistenceState(UserSessionModel.SessionPersistenceState persistenceState);
}

View file

@ -81,7 +81,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
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
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<MapUserSessionEntity> 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<String, AuthenticatedClientSessionModel> 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;
}
}

View file

@ -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);
}