Store time as seconds as a long in map store

This avoids overflowing the value in 2038.

Closes #10960
This commit is contained in:
Alexander Schwartz
2022-04-06 14:41:17 +02:00
committed by Hynek Mlnařík
parent 4def2d83e0
commit 5c1a8d401d
34 changed files with 218 additions and 100 deletions

View File

@@ -49,7 +49,7 @@ public class HotRodAuthenticationSessionEntity extends AbstractHotRodEntity {
public String authUserId;
@ProtoField(number = 4)
public Integer timestamp;
public Long timestamp;
@ProtoField(number = 5)
public String redirectUri;

View File

@@ -49,7 +49,7 @@ public class HotRodRootAuthenticationSessionEntity extends AbstractHotRodEntity
public String realmId;
@ProtoField(number = 4)
public Integer timestamp;
public Long timestamp;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 5)

View File

@@ -108,7 +108,7 @@ public class HotRodClientEntity extends AbstractHotRodEntity {
public Boolean frontchannelLogout;
@ProtoField(number = 20)
public Integer notBefore;
public Long notBefore;
@ProtoField(number = 21)
public Set<String> scope;

View File

@@ -46,7 +46,7 @@ public class HotRodUserLoginFailureEntity extends AbstractHotRodEntity {
public String userId;
@ProtoField(number = 5)
public Integer failedLoginNotBefore;
public Long failedLoginNotBefore;
@ProtoField(number = 6)
public Integer numFailures;

View File

@@ -133,7 +133,7 @@ public class HotRodRealmEntity extends AbstractHotRodEntity {
@ProtoField(number = 29)
public Integer clientSessionMaxLifespan;
@ProtoField(number = 30)
public Integer notBefore;
public Long notBefore;
@ProtoField(number = 31)
public Integer offlineSessionIdleTimeout;
@ProtoField(number = 32)

View File

@@ -13,11 +13,11 @@ public class HotRodClientInitialAccessEntity extends AbstractHotRodEntity {
@ProtoField(number = 2)
public Integer count;
@ProtoField(number = 3)
public Integer expiration;
public Long expiration;
@ProtoField(number = 4)
public Integer remainingCount;
@ProtoField(number = 5)
public Integer timestamp;
public Long timestamp;
@Override
public boolean equals(Object o) {
return HotRodClientInitialAccessEntityDelegate.entityEquals(this, o);

View File

@@ -153,7 +153,7 @@ public class HotRodUserEntity extends AbstractHotRodEntity {
public String serviceAccountClientLink;
@ProtoField(number = 21)
public Integer notBefore;
public Long notBefore;
public static abstract class AbstractHotRodUserEntityDelegate extends UpdatableHotRodEntityDelegateImpl<HotRodUserEntity> implements MapUserEntity {

View File

@@ -153,12 +153,12 @@ public class JpaAuthenticationSessionEntity extends UpdatableEntity.Impl impleme
}
@Override
public Integer getTimestamp() {
public Long getTimestamp() {
return metadata.getTimestamp();
}
@Override
public void setTimestamp(Integer timestamp) {
public void setTimestamp(Long timestamp) {
metadata.setTimestamp(timestamp);
}

View File

@@ -78,7 +78,7 @@ public class JpaRootAuthenticationSessionEntity extends AbstractRootAuthenticati
@Column(insertable = false, updatable = false)
@Basic(fetch = FetchType.LAZY)
private Integer timestamp;
private Long timestamp;
@Column(insertable = false, updatable = false)
@Basic(fetch = FetchType.LAZY)
@@ -102,7 +102,7 @@ public class JpaRootAuthenticationSessionEntity extends AbstractRootAuthenticati
* Used by hibernate when calling cb.construct from read(QueryParameters) method.
* It is used to select root auth session without metadata(json) field.
*/
public JpaRootAuthenticationSessionEntity(UUID id, Integer entityVersion, String realmId, Integer timestamp, Long expiration) {
public JpaRootAuthenticationSessionEntity(UUID id, Integer entityVersion, String realmId, Long timestamp, Long expiration) {
this.id = id;
this.entityVersion = entityVersion;
this.realmId = realmId;
@@ -158,13 +158,13 @@ public class JpaRootAuthenticationSessionEntity extends AbstractRootAuthenticati
}
@Override
public Integer getTimestamp() {
public Long getTimestamp() {
if (isMetadataInitialized()) return metadata.getTimestamp();
return timestamp;
}
@Override
public void setTimestamp(Integer timestamp) {
public void setTimestamp(Long timestamp) {
metadata.setTimestamp(timestamp);
}

View File

@@ -363,12 +363,12 @@ public class JpaClientEntity extends AbstractClientEntity implements JpaRootVers
}
@Override
public Integer getNotBefore() {
public Long getNotBefore() {
return metadata.getNotBefore();
}
@Override
public void setNotBefore(Integer notBefore) {
public void setNotBefore(Long notBefore) {
metadata.setNotBefore(notBefore);
}

View File

@@ -37,7 +37,7 @@ limitations under the License.
<ext:addGeneratedColumn tableName="kc_auth_root_session">
<ext:column name="entityversion" type="INTEGER" jsonColumn="metadata" jsonProperty="entityVersion"/>
<ext:column name="realmid" type="VARCHAR(36)" jsonColumn="metadata" jsonProperty="fRealmId"/>
<ext:column name="timestamp" type="INTEGER" jsonColumn="metadata" jsonProperty="fTimestamp"/>
<ext:column name="timestamp" type="BIGINT" jsonColumn="metadata" jsonProperty="fTimestamp"/>
<ext:column name="expiration" type="BIGINT" jsonColumn="metadata" jsonProperty="fExpiration"/>
</ext:addGeneratedColumn>
<createIndex tableName="kc_auth_root_session" indexName="auth_root_session_entityVersion">

View File

@@ -44,8 +44,8 @@ public interface MapAuthenticationSessionEntity extends UpdatableEntity {
String getAuthUserId();
void setAuthUserId(String authUserId);
Integer getTimestamp();
void setTimestamp(Integer timestamp);
Long getTimestamp();
void setTimestamp(Long timestamp);
String getRedirectUri();
void setRedirectUri(String redirectUri);

View File

@@ -22,6 +22,7 @@ import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.utils.SessionExpiration;
import org.keycloak.sessions.AuthenticationSessionModel;
@@ -52,12 +53,12 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
@Override
public int getTimestamp() {
return entity.getTimestamp();
return TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(entity.getTimestamp());
}
@Override
public void setTimestamp(int timestamp) {
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
}
@@ -84,14 +85,14 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
authSessionEntity.setClientUUID(client.getId());
int timestamp = Time.currentTime();
authSessionEntity.setTimestamp(timestamp);
authSessionEntity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
String tabId = generateTabId();
authSessionEntity.setTabId(tabId);
entity.addAuthenticationSession(authSessionEntity);
// Update our timestamp when adding new authenticationSession
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
return entity.getAuthenticationSession(tabId).map(this::toAdapter).map(this::setAuthContext).orElse(null);
@@ -105,7 +106,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
session.authenticationSessions().removeRootAuthenticationSession(realm, this);
} else {
int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
}
}
@@ -115,7 +116,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
public void restartSession(RealmModel realm) {
entity.setAuthenticationSessions(null);
int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
}

View File

@@ -84,8 +84,8 @@ public interface MapRootAuthenticationSessionEntity extends AbstractEntity, Upda
String getRealmId();
void setRealmId(String realmId);
Integer getTimestamp();
void setTimestamp(Integer timestamp);
Long getTimestamp();
void setTimestamp(Long timestamp);
Long getExpiration();
void setExpiration(Long expiration);

View File

@@ -23,6 +23,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
@@ -99,7 +100,7 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
entity.setId(id);
entity.setRealmId(realm.getId());
int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
if (id != null && tx.read(id) != null) {

View File

@@ -22,6 +22,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.ProtocolMapperModel;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.utils.KeycloakModelUtils;
import java.security.MessageDigest;
import java.util.Collection;
@@ -418,13 +419,13 @@ public abstract class MapClientAdapter extends AbstractClientModel<MapClientEnti
@Override
public int getNotBefore() {
final Integer notBefore = entity.getNotBefore();
return notBefore == null ? 0 : notBefore;
final Long notBefore = entity.getNotBefore();
return notBefore == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(notBefore);
}
@Override
public void setNotBefore(int notBefore) {
entity.setNotBefore(notBefore);
entity.setNotBefore(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(notBefore));
}
/*************** Scopes mappings ****************/

View File

@@ -120,7 +120,7 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity, Entity
Integer getNodeReRegistrationTimeout();
Integer getNotBefore();
Long getNotBefore();
String getProtocol();
@@ -188,7 +188,7 @@ public interface MapClientEntity extends AbstractEntity, UpdatableEntity, Entity
void setNodeReRegistrationTimeout(Integer nodeReRegistrationTimeout);
void setNotBefore(Integer notBefore);
void setNotBefore(Long notBefore);
void setProtocol(String protocol);

View File

@@ -27,8 +27,11 @@ import org.keycloak.models.ModelDuplicateException;
import org.keycloak.models.RealmModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
@@ -56,9 +59,9 @@ public class MapClientProvider implements ClientProvider {
private static final Logger LOG = Logger.getLogger(MapClientProvider.class);
private final KeycloakSession session;
final MapKeycloakTransaction<MapClientEntity, ClientModel> tx;
private final ConcurrentMap<String, ConcurrentMap<String, Integer>> clientRegisteredNodesStore;
private final ConcurrentMap<String, ConcurrentMap<String, Long>> clientRegisteredNodesStore;
public MapClientProvider(KeycloakSession session, MapStorage<MapClientEntity, ClientModel> clientStore, ConcurrentMap<String, ConcurrentMap<String, Integer>> clientRegisteredNodesStore) {
public MapClientProvider(KeycloakSession session, MapStorage<MapClientEntity, ClientModel> clientStore, ConcurrentMap<String, ConcurrentMap<String, Long>> clientRegisteredNodesStore) {
this.session = session;
this.clientRegisteredNodesStore = clientRegisteredNodesStore;
this.tx = clientStore.createTransaction(session);
@@ -92,18 +95,25 @@ public class MapClientProvider implements ClientProvider {
/** This is runtime information and should have never been part of the adapter */
@Override
public Map<String, Integer> getRegisteredNodes() {
return clientRegisteredNodesStore.computeIfAbsent(entity.getId(), k -> new ConcurrentHashMap<>());
return Collections.unmodifiableMap(getMapForEntity()
.entrySet()
.stream()
.collect(Collectors.toMap(Map.Entry::getKey, e -> TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(e.getValue())))
);
}
@Override
public void registerNode(String nodeHost, int registrationTime) {
Map<String, Integer> value = getRegisteredNodes();
value.put(nodeHost, registrationTime);
getMapForEntity().put(nodeHost, TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(registrationTime));
}
@Override
public void unregisterNode(String nodeHost) {
getRegisteredNodes().remove(nodeHost);
getMapForEntity().remove(nodeHost);
}
private ConcurrentMap<String, Long> getMapForEntity() {
return clientRegisteredNodesStore.computeIfAbsent(entity.getId(), k -> new ConcurrentHashMap<>());
}
};

View File

@@ -37,7 +37,7 @@ import java.util.concurrent.ConcurrentMap;
*/
public class MapClientProviderFactory extends AbstractMapProviderFactory<ClientProvider, MapClientEntity, ClientModel> implements ClientProviderFactory, ProviderEventListener {
private final ConcurrentHashMap<String, ConcurrentMap<String, Integer>> REGISTERED_NODES_STORE = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, ConcurrentMap<String, Long>> REGISTERED_NODES_STORE = new ConcurrentHashMap<>();
private Runnable onClose;

View File

@@ -0,0 +1,55 @@
/*
* Copyright 2022. Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.map.common;
import org.jboss.logging.Logger;
/**
* Wrapper for adapters around handling time in seconds.
*
* Will be removed once <a href="https://github.com/keycloak/keycloak/issues/11053">#11053</a> has been implemented.
* @author Alexander Schwartz
*/
public class TimeAdapter {
private static final Logger LOG = Logger.getLogger(TimeAdapter.class);
/**
* Wrapper to all unsafe downgrading from a Long to an Integer while Keycloak core still handles all time since 1970 as seconds as integers.
* This is safer to use than downgrading in several places as that might be missed once the Core starts to use longs as timestamps as well.
* Simplify/remove once <a href="https://github.com/keycloak/keycloak/issues/11053">#11053</a> has been implemented.
*/
public static int fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(Long timestamp) {
if (timestamp > Integer.MAX_VALUE) {
LOG.warn("Trimmed time value found in the map store; value too large and not supported in core");
return Integer.MAX_VALUE;
} else {
return timestamp.intValue();
}
}
/**
* Wrapper to all upgrading from an Integer to a Long while Keycloak core still handles all time seconds since 1970 as seconds as integers.
* This is safer to use and remove once the Core starts to use longs as timestamps as well.
* Simplify/remove once <a href="https://github.com/keycloak/keycloak/issues/11053">#11053</a> has been implemented.
*/
public static long fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(int timestamp) {
return timestamp;
}
}

View File

@@ -18,6 +18,7 @@ package org.keycloak.models.map.loginFailure;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.map.common.TimeAdapter;
/**
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
@@ -39,13 +40,13 @@ public class MapUserLoginFailureAdapter extends AbstractUserLoginFailureModel<Ma
@Override
public int getFailedLoginNotBefore() {
Integer failedLoginNotBefore = entity.getFailedLoginNotBefore();
return failedLoginNotBefore == null ? 0 : failedLoginNotBefore;
Long failedLoginNotBefore = entity.getFailedLoginNotBefore();
return failedLoginNotBefore == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(failedLoginNotBefore);
}
@Override
public void setFailedLoginNotBefore(int notBefore) {
entity.setFailedLoginNotBefore(notBefore);
entity.setFailedLoginNotBefore(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(notBefore));
}
@Override

View File

@@ -64,8 +64,8 @@ public interface MapUserLoginFailureEntity extends AbstractEntity, UpdatableEnti
String getUserId();
void setUserId(String userId);
Integer getFailedLoginNotBefore();
void setFailedLoginNotBefore(Integer failedLoginNotBefore);
Long getFailedLoginNotBefore();
void setFailedLoginNotBefore(Long failedLoginNotBefore);
Integer getNumFailures();
void setNumFailures(Integer numFailures);

View File

@@ -56,6 +56,7 @@ import org.keycloak.models.RequiredActionProviderModel;
import org.keycloak.models.RequiredCredentialModel;
import org.keycloak.models.RoleModel;
import org.keycloak.models.WebAuthnPolicy;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.realm.entity.MapAuthenticationExecutionEntity;
import org.keycloak.models.map.realm.entity.MapAuthenticationFlowEntity;
import org.keycloak.models.map.realm.entity.MapAuthenticatorConfigEntity;
@@ -1229,13 +1230,13 @@ public class MapRealmAdapter extends AbstractRealmModel<MapRealmEntity> implemen
@Override
public int getNotBefore() {
Integer i = entity.getNotBefore();
return i == null ? 0 : i;
Long notBefore = entity.getNotBefore();
return notBefore == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(notBefore);
}
@Override
public void setNotBefore(int notBefore) {
entity.setNotBefore(notBefore);
entity.setNotBefore(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(notBefore));
}
@Override

View File

@@ -332,8 +332,8 @@ public interface MapRealmEntity extends UpdatableEntity, AbstractEntity, EntityW
Integer getAccessCodeLifespanLogin();
void setAccessCodeLifespanLogin(Integer accessCodeLifespanLogin);
Integer getNotBefore();
void setNotBefore(Integer notBefore);
Long getNotBefore();
void setNotBefore(Long notBefore);
Integer getClientSessionIdleTimeout();
void setClientSessionIdleTimeout(Integer clientSessionIdleTimeout);

View File

@@ -19,6 +19,7 @@ package org.keycloak.models.map.realm.entity;
import org.keycloak.common.util.Time;
import org.keycloak.models.ClientInitialAccessModel;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.annotations.GenerateEntityImplementations;
import org.keycloak.models.map.common.AbstractEntity;
import org.keycloak.models.map.common.DeepCloner;
@@ -33,8 +34,8 @@ public interface MapClientInitialAccessEntity extends UpdatableEntity, AbstractE
MapClientInitialAccessEntity entity = new MapClientInitialAccessEntityImpl();
entity.setId(KeycloakModelUtils.generateId());
entity.setTimestamp(currentTime);
entity.setExpiration(expiration);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
entity.setExpiration(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(expiration));
entity.setCount(count);
entity.setRemainingCount(count);
return entity;
@@ -44,10 +45,10 @@ public interface MapClientInitialAccessEntity extends UpdatableEntity, AbstractE
if (entity == null) return null;
ClientInitialAccessModel model = new ClientInitialAccessModel();
model.setId(entity.getId());
Integer timestamp = entity.getTimestamp();
model.setTimestamp(timestamp == null ? 0 : timestamp);
Integer expiration = entity.getExpiration();
model.setExpiration(expiration == null ? 0 : expiration);
Long timestamp = entity.getTimestamp();
model.setTimestamp(timestamp == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(timestamp));
Long expiration = entity.getExpiration();
model.setExpiration(expiration == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(expiration));
Integer count = entity.getCount();
model.setCount(count == null ? 0 : count);
Integer remainingCount = entity.getRemainingCount();
@@ -55,11 +56,11 @@ public interface MapClientInitialAccessEntity extends UpdatableEntity, AbstractE
return model;
}
Integer getTimestamp();
void setTimestamp(Integer timestamp);
Long getTimestamp();
void setTimestamp(Long timestamp);
Integer getExpiration();
void setExpiration(Integer expiration);
Long getExpiration();
void setExpiration(Long expiration);
Integer getCount();
void setCount(Integer count);

View File

@@ -244,6 +244,6 @@ public interface MapUserEntity extends UpdatableEntity, AbstractEntity, EntityWi
String getServiceAccountClientLink();
void setServiceAccountClientLink(String serviceAccountClientLink);
Integer getNotBefore();
void setNotBefore(Integer notBefore);
Long getNotBefore();
void setNotBefore(Long notBefore);
}

View File

@@ -41,6 +41,7 @@ import org.keycloak.models.UserConsentModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserModel.SearchableFields;
import org.keycloak.models.UserProvider;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
@@ -278,17 +279,17 @@ public class MapUserProvider implements UserProvider.Streams, UserCredentialStor
@Override
public void setNotBeforeForUser(RealmModel realm, UserModel user, int notBefore) {
LOG.tracef("setNotBeforeForUser(%s, %s, %d)%s", realm, user.getId(), notBefore, getShortStackTrace());
getEntityByIdOrThrow(realm, user.getId()).setNotBefore(notBefore);
getEntityByIdOrThrow(realm, user.getId()).setNotBefore(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(notBefore));
}
@Override
public int getNotBeforeOfUser(RealmModel realm, UserModel user) {
LOG.tracef("getNotBeforeOfUser(%s, %s)%s", realm, user.getId(), getShortStackTrace());
Integer notBefore = getEntityById(realm, user.getId())
Long notBefore = getEntityById(realm, user.getId())
.orElseThrow(this::userDoesntExistException)
.getNotBefore();
return notBefore == null ? 0 : notBefore;
return notBefore == null ? 0 : TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(notBefore);
}
@Override

View File

@@ -20,6 +20,7 @@ import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.common.TimeAdapter;
import java.util.Collections;
import java.util.Map;
@@ -41,13 +42,13 @@ public abstract class MapAuthenticatedClientSessionAdapter extends AbstractAuthe
@Override
public int getTimestamp() {
Integer timestamp = entity.getTimestamp();
return timestamp != null ? timestamp : 0;
Long timestamp = entity.getTimestamp();
return timestamp != null ? TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(timestamp) : 0;
}
@Override
public void setTimestamp(int timestamp) {
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
}
@Override

View File

@@ -32,7 +32,7 @@ import java.util.Map;
@DeepCloner.Root
public interface MapAuthenticatedClientSessionEntity extends AbstractEntity, UpdatableEntity {
public abstract class AbstractAuthenticatedClientSessionEntity extends UpdatableEntity.Impl implements MapAuthenticatedClientSessionEntity {
abstract class AbstractAuthenticatedClientSessionEntity extends UpdatableEntity.Impl implements MapAuthenticatedClientSessionEntity {
private String id;
@@ -64,8 +64,8 @@ public interface MapAuthenticatedClientSessionEntity extends AbstractEntity, Upd
String getRedirectUri();
void setRedirectUri(String redirectUri);
Integer getTimestamp();
void setTimestamp(Integer timestamp);
Long getTimestamp();
void setTimestamp(Long timestamp);
Long getExpiration();
void setExpiration(Long expiration);

View File

@@ -24,6 +24,8 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.map.common.TimeAdapter;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedList;
@@ -89,19 +91,19 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
@Override
public int getStarted() {
Integer started = entity.getStarted();
return started != null ? started : 0;
Long started = entity.getStarted();
return started != null ? TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(started) : 0;
}
@Override
public int getLastSessionRefresh() {
Integer lastSessionRefresh = entity.getLastSessionRefresh();
return lastSessionRefresh != null ? lastSessionRefresh : 0;
Long lastSessionRefresh = entity.getLastSessionRefresh();
return lastSessionRefresh != null ? TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(lastSessionRefresh) : 0;
}
@Override
public void setLastSessionRefresh(int seconds) {
entity.setLastSessionRefresh(seconds);
entity.setLastSessionRefresh(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(seconds));
}
@Override
@@ -214,8 +216,8 @@ public abstract class MapUserSessionAdapter extends AbstractUserSessionModel {
entity.setBrokerUserId(brokerUserId);
int currentTime = Time.currentTime();
entity.setStarted(currentTime);
entity.setLastSessionRefresh(currentTime);
entity.setStarted(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
entity.setLastSessionRefresh(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
entity.setState(null);

View File

@@ -34,7 +34,7 @@ import java.util.Map;
@DeepCloner.Root
public interface MapUserSessionEntity extends AbstractEntity, UpdatableEntity {
public abstract class AbstractUserSessionEntity extends UpdatableEntity.Impl implements MapUserSessionEntity {
abstract class AbstractUserSessionEntity extends UpdatableEntity.Impl implements MapUserSessionEntity {
private String id;
@@ -75,12 +75,11 @@ public interface MapUserSessionEntity extends AbstractEntity, UpdatableEntity {
Boolean isRememberMe();
void setRememberMe(Boolean rememberMe);
Integer getStarted();
void setStarted(Integer started);
Long getStarted();
void setStarted(Long started);
Integer getLastSessionRefresh();
void setLastSessionRefresh(Integer lastSessionRefresh);
Long getLastSessionRefresh();
void setLastSessionRefresh(Long lastSessionRefresh);
Long getExpiration();
void setExpiration(Long expiration);

View File

@@ -27,6 +27,7 @@ import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.UserSessionProvider;
import org.keycloak.models.map.common.TimeAdapter;
import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
@@ -81,7 +82,7 @@ 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) -> {
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0l;
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0L;
if (expiration <= Time.currentTime()) {
if (Objects.equals(origEntity.getPersistenceState(), TRANSIENT)) {
transientUserSessions.remove(origEntity.getId());
@@ -97,7 +98,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
@Override
public void setLastSessionRefresh(int lastSessionRefresh) {
entity.setLastSessionRefresh(lastSessionRefresh);
entity.setLastSessionRefresh(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(lastSessionRefresh));
// whenever the lastSessionRefresh is changed recompute the expiration time
setUserSessionExpiration(entity, realm);
}
@@ -111,7 +112,7 @@ 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 -> {
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0l;
long expiration = origEntity.getExpiration() != null ? origEntity.getExpiration() : 0L;
if (expiration <= Time.currentTime()) {
userSession.removeAuthenticatedClientSessions(Arrays.asList(origEntity.getClientId()));
clientSessionTx.delete(origEntity.getId());
@@ -127,7 +128,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
@Override
public void setTimestamp(int timestamp) {
entity.setTimestamp(timestamp);
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(timestamp));
// whenever the timestamp is changed recompute the expiration time
setClientSessionExpiration(entity, realm, client);
}
@@ -424,8 +425,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
userSession.setNote(CORRESPONDING_SESSION_ID, offlineUserSession.getId());
int currentTime = Time.currentTime();
offlineUserSession.setStarted(currentTime);
offlineUserSession.setLastSessionRefresh(currentTime);
offlineUserSession.setStarted(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
offlineUserSession.setLastSessionRefresh(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
setUserSessionExpiration(offlineUserSession, userSession.getRealm());
return userEntityToAdapterFunc(userSession.getRealm()).apply(offlineUserSession);
@@ -467,7 +468,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
MapAuthenticatedClientSessionEntity clientSessionEntity = createAuthenticatedClientSessionInstance(clientSession, offlineUserSession, true);
int currentTime = Time.currentTime();
clientSessionEntity.setNote(AuthenticatedClientSessionModel.STARTED_AT_NOTE, String.valueOf(currentTime));
clientSessionEntity.setTimestamp(currentTime);
clientSessionEntity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(currentTime));
setClientSessionExpiration(clientSessionEntity, clientSession.getRealm(), clientSession.getClient());
clientSessionEntity = clientSessionTx.create(clientSessionEntity);
@@ -631,8 +632,8 @@ public class MapUserSessionProvider implements UserSessionProvider {
entity.setNotes(new ConcurrentHashMap<>(userSession.getNotes()));
entity.setNote(CORRESPONDING_SESSION_ID, userSession.getId());
entity.setState(userSession.getState());
entity.setStarted(userSession.getStarted());
entity.setLastSessionRefresh(userSession.getLastSessionRefresh());
entity.setStarted(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(userSession.getStarted()));
entity.setLastSessionRefresh(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(userSession.getLastSessionRefresh()));
return entity;
}
@@ -647,7 +648,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
entity.setNotes(new ConcurrentHashMap<>(clientSession.getNotes()));
entity.setRedirectUri(clientSession.getRedirectUri());
entity.setTimestamp(clientSession.getTimestamp());
entity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(clientSession.getTimestamp()));
return entity;
}
@@ -666,7 +667,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
userSessionEntity.setBrokerSessionId(brokerSessionId);
userSessionEntity.setBrokerUserId(brokerUserId);
userSessionEntity.setOffline(offline);
userSessionEntity.setStarted(Time.currentTime());
userSessionEntity.setStarted(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(Time.currentTime()));
userSessionEntity.setLastSessionRefresh(userSessionEntity.getStarted());
return userSessionEntity;
}
@@ -679,7 +680,7 @@ public class MapUserSessionProvider implements UserSessionProvider {
clientSessionEntity.setRealmId(realmId);
clientSessionEntity.setClientId(clientId);
clientSessionEntity.setOffline(offline);
clientSessionEntity.setTimestamp(Time.currentTime());
clientSessionEntity.setTimestamp(TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(Time.currentTime()));
return clientSessionEntity;
}

View File

@@ -27,7 +27,7 @@ import org.keycloak.protocol.oidc.OIDCConfigAttributes;
public class SessionExpiration {
public static void setClientSessionExpiration(MapAuthenticatedClientSessionEntity entity, RealmModel realm, ClientModel client) {
long timestamp = entity.getTimestamp() != null ? entity.getTimestamp() : 0l;
long timestamp = entity.getTimestamp() != null ? entity.getTimestamp() : 0L;
if (Boolean.TRUE.equals(entity.isOffline())) {
long sessionExpires = timestamp + realm.getOfflineSessionIdleTimeout();
if (realm.isOfflineSessionMaxLifespanEnabled()) {
@@ -101,8 +101,8 @@ public class SessionExpiration {
}
public static void setUserSessionExpiration(MapUserSessionEntity entity, RealmModel realm) {
int started = entity.getStarted() != null ? entity.getStarted() : 0;
long lastSessionRefresh = entity.getLastSessionRefresh() != null ? entity.getLastSessionRefresh() : 0l;
long started = entity.getStarted() != null ? entity.getStarted() : 0L;
long lastSessionRefresh = entity.getLastSessionRefresh() != null ? entity.getLastSessionRefresh() : 0L;
if (Boolean.TRUE.equals(entity.isOffline())) {
long sessionExpires = lastSessionRefresh + realm.getOfflineSessionIdleTimeout();
if (realm.isOfflineSessionMaxLifespanEnabled()) {
@@ -127,7 +127,7 @@ public class SessionExpiration {
entity.setExpiration(Math.min(expiration, sessionExpires));
} else {
long sessionExpires = (long) started
long sessionExpires = started
+ (Boolean.TRUE.equals(entity.isRememberMe()) && realm.getSsoSessionMaxLifespanRememberMe() > 0
? realm.getSsoSessionMaxLifespanRememberMe()
: realm.getSsoSessionMaxLifespan());

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2022. Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.models.map.common;
import org.junit.Assert;
import org.junit.Test;
/**
* @author Alexander Schwartz
*/
public class TimeAdapterTest {
@Test
public void shouldConvertIntegersToLongs() {
Assert.assertEquals(0, TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(0));
Assert.assertEquals(1, TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(1));
Assert.assertEquals(Integer.MAX_VALUE, TimeAdapter.fromIntegerWithTimeInSecondsToLongWithTimeAsInSeconds(Integer.MAX_VALUE));
}
@Test
public void shouldConvertLongsToIntegersSafely() {
Assert.assertEquals(0, TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds((long) 0));
Assert.assertEquals(1, TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds((long) 1));
Assert.assertEquals(Integer.MAX_VALUE, TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds((long) Integer.MAX_VALUE));
Assert.assertEquals(Integer.MAX_VALUE, TimeAdapter.fromLongWithTimeInSecondsToIntegerWithTimeInSeconds(Long.MAX_VALUE));
}
}