Add expiration field to root authentication session

This commit is contained in:
Martin Kanis 2022-03-14 10:02:31 +01:00 committed by Hynek Mlnařík
parent f8ded02bef
commit e493b08fa7
15 changed files with 86 additions and 51 deletions

View file

@ -36,7 +36,7 @@ import org.keycloak.models.sessions.infinispan.events.RealmRemovedSessionEvent;
import org.keycloak.models.sessions.infinispan.events.SessionEventsSenderTransaction; import org.keycloak.models.sessions.infinispan.events.SessionEventsSenderTransaction;
import org.keycloak.models.sessions.infinispan.stream.RootAuthenticationSessionPredicate; import org.keycloak.models.sessions.infinispan.stream.RootAuthenticationSessionPredicate;
import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator; import org.keycloak.models.sessions.infinispan.util.InfinispanKeyGenerator;
import org.keycloak.models.utils.RealmInfoUtil; import org.keycloak.models.utils.SessionExpiration;
import org.keycloak.sessions.AuthenticationSessionCompoundId; import org.keycloak.sessions.AuthenticationSessionCompoundId;
import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.sessions.RootAuthenticationSessionModel; import org.keycloak.sessions.RootAuthenticationSessionModel;
@ -83,7 +83,7 @@ public class InfinispanAuthenticationSessionProvider implements AuthenticationSe
entity.setRealmId(realm.getId()); entity.setRealmId(realm.getId());
entity.setTimestamp(Time.currentTime()); entity.setTimestamp(Time.currentTime());
int expirationSeconds = RealmInfoUtil.getDettachedClientSessionLifespan(realm); int expirationSeconds = SessionExpiration.getAuthSessionLifespan(realm);
tx.put(cache, id, entity, expirationSeconds, TimeUnit.SECONDS); tx.put(cache, id, entity, expirationSeconds, TimeUnit.SECONDS);
return wrap(realm, entity); return wrap(realm, entity);

View file

@ -31,7 +31,7 @@ import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.sessions.infinispan.entities.AuthenticationSessionEntity; import org.keycloak.models.sessions.infinispan.entities.AuthenticationSessionEntity;
import org.keycloak.models.sessions.infinispan.entities.RootAuthenticationSessionEntity; import org.keycloak.models.sessions.infinispan.entities.RootAuthenticationSessionEntity;
import org.keycloak.models.utils.RealmInfoUtil; import org.keycloak.models.utils.SessionExpiration;
import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.AuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel; import org.keycloak.sessions.RootAuthenticationSessionModel;
@ -63,7 +63,7 @@ public class RootAuthenticationSessionAdapter implements RootAuthenticationSessi
} }
void update() { void update() {
int expirationSeconds = RealmInfoUtil.getDettachedClientSessionLifespan(realm); int expirationSeconds = SessionExpiration.getAuthSessionLifespan(realm);
provider.tx.replace(cache, entity.getId(), entity, expirationSeconds, TimeUnit.SECONDS); provider.tx.replace(cache, entity.getId(), entity, expirationSeconds, TimeUnit.SECONDS);
} }

View file

@ -48,11 +48,14 @@ public class HotRodRootAuthenticationSessionEntity extends AbstractHotRodEntity
@ProtoField(number = 3) @ProtoField(number = 3)
public String realmId; public String realmId;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 4) @ProtoField(number = 4)
public Integer timestamp; public Integer timestamp;
@ProtoDoc("@Field(index = Index.YES, store = Store.YES)")
@ProtoField(number = 5) @ProtoField(number = 5)
public Long expiration;
@ProtoField(number = 6)
public Set<HotRodAuthenticationSessionEntity> authenticationSessions; public Set<HotRodAuthenticationSessionEntity> authenticationSessions;
public static abstract class AbstractHotRodRootAuthenticationSessionEntityDelegate extends UpdatableHotRodEntityDelegateImpl<HotRodRootAuthenticationSessionEntity> implements MapRootAuthenticationSessionEntity { public static abstract class AbstractHotRodRootAuthenticationSessionEntityDelegate extends UpdatableHotRodEntityDelegateImpl<HotRodRootAuthenticationSessionEntity> implements MapRootAuthenticationSessionEntity {

View file

@ -22,6 +22,7 @@ import org.keycloak.common.util.Time;
import org.keycloak.models.ClientModel; import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.utils.SessionExpiration;
import org.keycloak.sessions.AuthenticationSessionModel; import org.keycloak.sessions.AuthenticationSessionModel;
import java.util.Collections; import java.util.Collections;
@ -57,6 +58,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
@Override @Override
public void setTimestamp(int timestamp) { public void setTimestamp(int timestamp) {
entity.setTimestamp(timestamp); entity.setTimestamp(timestamp);
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
} }
@Override @Override
@ -90,6 +92,7 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
// Update our timestamp when adding new authenticationSession // Update our timestamp when adding new authenticationSession
entity.setTimestamp(timestamp); entity.setTimestamp(timestamp);
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
return entity.getAuthenticationSession(tabId).map(this::toAdapter).map(this::setAuthContext).orElse(null); return entity.getAuthenticationSession(tabId).map(this::toAdapter).map(this::setAuthContext).orElse(null);
} }
@ -101,7 +104,9 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
if (entity.getAuthenticationSessions().isEmpty()) { if (entity.getAuthenticationSessions().isEmpty()) {
session.authenticationSessions().removeRootAuthenticationSession(realm, this); session.authenticationSessions().removeRootAuthenticationSession(realm, this);
} else { } else {
entity.setTimestamp(Time.currentTime()); int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
} }
} }
} }
@ -109,7 +114,9 @@ public class MapRootAuthenticationSessionAdapter extends AbstractRootAuthenticat
@Override @Override
public void restartSession(RealmModel realm) { public void restartSession(RealmModel realm) {
entity.setAuthenticationSessions(null); entity.setAuthenticationSessions(null);
entity.setTimestamp(Time.currentTime()); int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
} }
private String generateTabId() { private String generateTabId() {

View file

@ -87,6 +87,9 @@ public interface MapRootAuthenticationSessionEntity extends AbstractEntity, Upda
Integer getTimestamp(); Integer getTimestamp();
void setTimestamp(Integer timestamp); void setTimestamp(Integer timestamp);
Long getExpiration();
void setExpiration(Long expiration);
Set<MapAuthenticationSessionEntity> getAuthenticationSessions(); Set<MapAuthenticationSessionEntity> getAuthenticationSessions();
void setAuthenticationSessions(Set<MapAuthenticationSessionEntity> authenticationSessions); void setAuthenticationSessions(Set<MapAuthenticationSessionEntity> authenticationSessions);
Optional<MapAuthenticationSessionEntity> getAuthenticationSession(String tabId); Optional<MapAuthenticationSessionEntity> getAuthenticationSession(String tabId);

View file

@ -27,12 +27,13 @@ import org.keycloak.models.map.storage.MapKeycloakTransaction;
import org.keycloak.models.map.storage.MapStorage; import org.keycloak.models.map.storage.MapStorage;
import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator; import org.keycloak.models.map.storage.ModelCriteriaBuilder.Operator;
import org.keycloak.models.map.storage.criteria.DefaultModelCriteria; import org.keycloak.models.map.storage.criteria.DefaultModelCriteria;
import org.keycloak.models.utils.RealmInfoUtil; import org.keycloak.models.utils.SessionExpiration;
import org.keycloak.sessions.AuthenticationSessionCompoundId; import org.keycloak.sessions.AuthenticationSessionCompoundId;
import org.keycloak.sessions.AuthenticationSessionProvider; import org.keycloak.sessions.AuthenticationSessionProvider;
import org.keycloak.sessions.RootAuthenticationSessionModel; import org.keycloak.sessions.RootAuthenticationSessionModel;
import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields; import org.keycloak.sessions.RootAuthenticationSessionModel.SearchableFields;
import java.util.Map; import java.util.Map;
import java.util.Objects; import java.util.Objects;
import java.util.function.Function; import java.util.function.Function;
@ -63,7 +64,15 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
private Function<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> entityToAdapterFunc(RealmModel realm) { private Function<MapRootAuthenticationSessionEntity, RootAuthenticationSessionModel> entityToAdapterFunc(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 -> new MapRootAuthenticationSessionAdapter(session, realm, origEntity); return origEntity -> {
//return new MapRootAuthenticationSessionAdapter(session, realm, origEntity);
if (Time.currentTime() < origEntity.getExpiration()) {
return new MapRootAuthenticationSessionAdapter(session, realm, origEntity);
} else {
tx.delete(origEntity.getId());
return null;
}
};
} }
private Predicate<MapRootAuthenticationSessionEntity> entityRealmFilter(String realmId) { private Predicate<MapRootAuthenticationSessionEntity> entityRealmFilter(String realmId) {
@ -89,7 +98,9 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
MapRootAuthenticationSessionEntity entity = new MapRootAuthenticationSessionEntityImpl(); MapRootAuthenticationSessionEntity entity = new MapRootAuthenticationSessionEntityImpl();
entity.setId(id); entity.setId(id);
entity.setRealmId(realm.getId()); entity.setRealmId(realm.getId());
entity.setTimestamp(Time.currentTime()); int timestamp = Time.currentTime();
entity.setTimestamp(timestamp);
entity.setExpiration(SessionExpiration.getAuthSessionExpiration(realm, timestamp));
if (id != null && tx.read(id) != null) { if (id != null && tx.read(id) != null) {
throw new ModelDuplicateException("Root authentication session exists: " + entity.getId()); throw new ModelDuplicateException("Root authentication session exists: " + entity.getId());
@ -131,11 +142,9 @@ public class MapRootAuthenticationSessionProvider implements AuthenticationSessi
Objects.requireNonNull(realm, "The provided realm can't be null!"); Objects.requireNonNull(realm, "The provided realm can't be null!");
LOG.debugf("Removing expired sessions"); LOG.debugf("Removing expired sessions");
int expired = Time.currentTime() - RealmInfoUtil.getDettachedClientSessionLifespan(realm);
DefaultModelCriteria<RootAuthenticationSessionModel> mcb = criteria(); DefaultModelCriteria<RootAuthenticationSessionModel> mcb = criteria();
mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId()) mcb = mcb.compare(SearchableFields.REALM_ID, Operator.EQ, realm.getId())
.compare(SearchableFields.TIMESTAMP, Operator.LT, expired); .compare(SearchableFields.EXPIRATION, Operator.LT, Time.currentTime());
long deletedCount = tx.delete(withCriteria(mcb)); long deletedCount = tx.delete(withCriteria(mcb));

View file

@ -141,7 +141,7 @@ public class MapFieldPredicates {
put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, MapUserEntity::getServiceAccountClientLink); put(USER_PREDICATES, UserModel.SearchableFields.SERVICE_ACCOUNT_CLIENT, MapUserEntity::getServiceAccountClientLink);
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, MapRootAuthenticationSessionEntity::getRealmId); put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.REALM_ID, MapRootAuthenticationSessionEntity::getRealmId);
put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.TIMESTAMP, MapRootAuthenticationSessionEntity::getTimestamp); put(AUTHENTICATION_SESSION_PREDICATES, RootAuthenticationSessionModel.SearchableFields.EXPIRATION, MapRootAuthenticationSessionEntity::getExpiration);
put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, predicateForKeyField(MapResourceServerEntity::getId)); put(AUTHZ_RESOURCE_SERVER_PREDICATES, ResourceServer.SearchableFields.ID, predicateForKeyField(MapResourceServerEntity::getId));

View file

@ -22,9 +22,9 @@ import org.keycloak.models.RealmModel;
/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/ */
public class RealmInfoUtil { public class SessionExpiration {
public static int getDettachedClientSessionLifespan(RealmModel realm) { public static int getAuthSessionLifespan(RealmModel realm) {
int lifespan = realm.getAccessCodeLifespanLogin(); int lifespan = realm.getAccessCodeLifespanLogin();
if (realm.getAccessCodeLifespanUserAction() > lifespan) { if (realm.getAccessCodeLifespanUserAction() > lifespan) {
lifespan = realm.getAccessCodeLifespanUserAction(); lifespan = realm.getAccessCodeLifespanUserAction();
@ -35,4 +35,8 @@ public class RealmInfoUtil {
return lifespan; return lifespan;
} }
public static long getAuthSessionExpiration(RealmModel realm, int timestamp) {
return (long) timestamp + getAuthSessionLifespan(realm);
}
} }

View file

@ -34,7 +34,7 @@ public interface RootAuthenticationSessionModel {
public static class SearchableFields { public static class SearchableFields {
public static final SearchableModelField<RootAuthenticationSessionModel> ID = new SearchableModelField<>("id", String.class); public static final SearchableModelField<RootAuthenticationSessionModel> ID = new SearchableModelField<>("id", String.class);
public static final SearchableModelField<RootAuthenticationSessionModel> REALM_ID = new SearchableModelField<>("realmId", String.class); public static final SearchableModelField<RootAuthenticationSessionModel> REALM_ID = new SearchableModelField<>("realmId", String.class);
public static final SearchableModelField<RootAuthenticationSessionModel> TIMESTAMP = new SearchableModelField<>("timestamp", Long.class); public static final SearchableModelField<RootAuthenticationSessionModel> EXPIRATION = new SearchableModelField<>("expiration", Long.class);
} }
/** /**
@ -57,6 +57,7 @@ public interface RootAuthenticationSessionModel {
/** /**
* Sets a timestamp when the root authentication session was created or updated. * Sets a timestamp when the root authentication session was created or updated.
* It also updates the expiration time for the root authentication session entity.
* @param timestamp {@code int} * @param timestamp {@code int}
*/ */
void setTimestamp(int timestamp); void setTimestamp(int timestamp);

View file

@ -52,6 +52,7 @@ import org.keycloak.testsuite.pages.RegisterPage;
import org.keycloak.testsuite.pages.VerifyEmailPage; import org.keycloak.testsuite.pages.VerifyEmailPage;
import org.keycloak.testsuite.updaters.UserAttributeUpdater; import org.keycloak.testsuite.updaters.UserAttributeUpdater;
import org.keycloak.testsuite.util.GreenMailRule; import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
import org.keycloak.testsuite.util.MailUtils; import org.keycloak.testsuite.util.MailUtils;
import org.keycloak.testsuite.util.SecondBrowser; import org.keycloak.testsuite.util.SecondBrowser;
import org.keycloak.testsuite.util.UserActionTokenBuilder; import org.keycloak.testsuite.util.UserActionTokenBuilder;
@ -91,6 +92,9 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
@Rule @Rule
public GreenMailRule greenMail = new GreenMailRule(); public GreenMailRule greenMail = new GreenMailRule();
@Rule
public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this);
@Page @Page
protected AppPage appPage; protected AppPage appPage;
@ -454,7 +458,7 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
events.poll(); events.poll();
try { try {
setTimeOffset(3600); setTimeOffset(360);
driver.navigate().to(verificationUrl.trim()); driver.navigate().to(verificationUrl.trim());
@ -990,7 +994,7 @@ public class RequiredActionEmailVerificationTest extends AbstractTestRealmKeyclo
String verificationUrl = getPasswordResetEmailLink(message); String verificationUrl = getPasswordResetEmailLink(message);
try { try {
setTimeOffset(3600); setTimeOffset(360);
driver.navigate().to(verificationUrl.trim()); driver.navigate().to(verificationUrl.trim());

View file

@ -8,13 +8,18 @@ import static org.keycloak.testsuite.broker.BrokerTestTools.getConsumerRoot;
import java.util.List; import java.util.List;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.keycloak.admin.client.resource.RealmResource; import org.keycloak.admin.client.resource.RealmResource;
import org.keycloak.representations.idm.ClientRepresentation; import org.keycloak.representations.idm.ClientRepresentation;
import org.keycloak.representations.idm.RealmRepresentation; import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTest { public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTest {
@Rule
public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this);
@Override @Override
protected BrokerConfiguration getBrokerConfiguration() { protected BrokerConfiguration getBrokerConfiguration() {
return KcOidcBrokerConfiguration.INSTANCE; return KcOidcBrokerConfiguration.INSTANCE;
@ -35,8 +40,8 @@ public class KcOidcBrokerWithConsentTest extends AbstractInitializedBaseBrokerTe
// Change timeouts on realm-with-broker to lower values // Change timeouts on realm-with-broker to lower values
RealmResource realmWithBroker = adminClient.realm(bc.consumerRealmName()); RealmResource realmWithBroker = adminClient.realm(bc.consumerRealmName());
RealmRepresentation realmRep = realmWithBroker.toRepresentation(); RealmRepresentation realmRep = realmWithBroker.toRepresentation();
realmRep.setAccessCodeLifespanLogin(30);; realmRep.setAccessCodeLifespanLogin(30);
realmRep.setAccessCodeLifespan(30); realmRep.setAccessCodeLifespan(300);
realmRep.setAccessCodeLifespanUserAction(30); realmRep.setAccessCodeLifespanUserAction(30);
realmWithBroker.update(realmRep); realmWithBroker.update(realmRep);
} }

View file

@ -60,6 +60,7 @@ import org.keycloak.testsuite.updaters.RealmAttributeUpdater;
import org.keycloak.testsuite.util.AdminClientUtil; import org.keycloak.testsuite.util.AdminClientUtil;
import org.keycloak.testsuite.util.ContainerAssume; import org.keycloak.testsuite.util.ContainerAssume;
import org.keycloak.testsuite.util.DroneUtils; import org.keycloak.testsuite.util.DroneUtils;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
import org.keycloak.testsuite.util.JavascriptBrowser; import org.keycloak.testsuite.util.JavascriptBrowser;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.Matchers; import org.keycloak.testsuite.util.Matchers;
@ -87,7 +88,6 @@ import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
import static org.keycloak.common.Profile.Feature.AUTHORIZATION;
import static org.keycloak.common.Profile.Feature.DYNAMIC_SCOPES; import static org.keycloak.common.Profile.Feature.DYNAMIC_SCOPES;
import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId; import static org.keycloak.testsuite.admin.ApiUtil.findClientByClientId;
import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT; import static org.keycloak.testsuite.util.OAuthClient.AUTH_SERVER_ROOT;
@ -165,6 +165,9 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
@Page @Page
protected LoginPasswordUpdatePage updatePasswordPage; protected LoginPasswordUpdatePage updatePasswordPage;
@Rule
public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this);
private static String userId; private static String userId;
private static String user2Id; private static String user2Id;
@ -762,9 +765,8 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
@Test @Test
public void loginExpiredCode() { public void loginExpiredCode() {
loginPage.open(); loginPage.open();
// authSession expired and removed from the storage
setTimeOffset(5000); setTimeOffset(5000);
// No explicitly call "removeExpired". Hence authSession will still exists, but will be expired
//testingClient.testing().removeExpired("test");
loginPage.login("login@test.com", "password"); loginPage.login("login@test.com", "password");
loginPage.assertCurrent(); loginPage.assertCurrent();
@ -772,35 +774,28 @@ public class LoginTest extends AbstractTestRealmKeycloakTest {
Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError()); Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError());
setTimeOffset(0); setTimeOffset(0);
events.expectLogin().user((String) null).session((String) null).error(Errors.EXPIRED_CODE).clearDetails() events.expectLogin().client((String) null).user((String) null).session((String) null).error(Errors.EXPIRED_CODE).clearDetails()
.assertEvent(); .assertEvent();
} }
// KEYCLOAK-1037 // KEYCLOAK-1037
@Test @Test
public void loginExpiredCodeWithExplicitRemoveExpired() { public void loginExpiredCodeWithExplicitRemoveExpired() {
getTestingClient().testing().setTestingInfinispanTimeService(); loginPage.open();
setTimeOffset(5000);
try { loginPage.login("login@test.com", "password");
loginPage.open();
setTimeOffset(5000);
// Explicitly call "removeExpired". Hence authSession won't exist, but will be restarted from the KC_RESTART
testingClient.testing().removeExpired("test");
loginPage.login("login@test.com", "password"); loginPage.assertCurrent();
//loginPage.assertCurrent(); Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError());
loginPage.assertCurrent();
Assert.assertEquals("Your login attempt timed out. Login will start from the beginning.", loginPage.getError()); setTimeOffset(0);
events.expectLogin().user((String) null).session((String) null).error(Errors.EXPIRED_CODE).clearDetails() events.expectLogin().user((String) null).session((String) null).error(Errors.EXPIRED_CODE).clearDetails()
.detail(Details.RESTART_AFTER_TIMEOUT, "true") .detail(Details.RESTART_AFTER_TIMEOUT, "true")
.client((String) null) .client((String) null)
.assertEvent(); .assertEvent();
} finally {
getTestingClient().testing().revertTestingInfinispanTimeService();
}
} }
@Test @Test

View file

@ -51,6 +51,7 @@ import org.keycloak.testsuite.pages.VerifyEmailPage;
import org.keycloak.testsuite.updaters.ClientAttributeUpdater; import org.keycloak.testsuite.updaters.ClientAttributeUpdater;
import org.keycloak.testsuite.util.BrowserTabUtil; import org.keycloak.testsuite.util.BrowserTabUtil;
import org.keycloak.testsuite.util.GreenMailRule; import org.keycloak.testsuite.util.GreenMailRule;
import org.keycloak.testsuite.util.InfinispanTestTimeServiceRule;
import org.keycloak.testsuite.util.MailUtils; import org.keycloak.testsuite.util.MailUtils;
import org.keycloak.testsuite.util.OAuthClient; import org.keycloak.testsuite.util.OAuthClient;
import org.keycloak.testsuite.util.RealmBuilder; import org.keycloak.testsuite.util.RealmBuilder;
@ -92,6 +93,9 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
private String userId; private String userId;
private UserRepresentation defaultUser; private UserRepresentation defaultUser;
@Rule
public InfinispanTestTimeServiceRule ispnTestTimeService = new InfinispanTestTimeServiceRule(this);
@Drone @Drone
@SecondBrowser @SecondBrowser
protected WebDriver driver2; protected WebDriver driver2;
@ -409,7 +413,7 @@ public class ResetPasswordTest extends AbstractTestRealmKeycloakTest {
String changePasswordUrl = MailUtils.getPasswordResetEmailLink(message); String changePasswordUrl = MailUtils.getPasswordResetEmailLink(message);
try { try {
setTimeOffset(1800 + 23); setTimeOffset(360);
driver.navigate().to(changePasswordUrl.trim()); driver.navigate().to(changePasswordUrl.trim());

View file

@ -93,6 +93,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
public void testLoginSessionsCRUD(KeycloakSession session) { public void testLoginSessionsCRUD(KeycloakSession session) {
AtomicReference<String> rootAuthSessionID = new AtomicReference<>(); AtomicReference<String> rootAuthSessionID = new AtomicReference<>();
AtomicReference<String> tabID = new AtomicReference<>(); AtomicReference<String> tabID = new AtomicReference<>();
final int timestamp = Time.currentTime();
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCRUD1) -> { KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCRUD1) -> {
KeycloakSession currentSession = sessionCRUD1; KeycloakSession currentSession = sessionCRUD1;
@ -107,7 +108,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
tabID.set(authSession.getTabId()); tabID.set(authSession.getTabId());
authSession.setAction("foo"); authSession.setAction("foo");
rootAuthSession.setTimestamp(100); rootAuthSession.setTimestamp(timestamp);
}); });
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCRUD2) -> { KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionCRUD2) -> {
@ -121,11 +122,11 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
AuthenticationSessionModel authSession = rootAuthSession.getAuthenticationSession(client1, tabID.get()); AuthenticationSessionModel authSession = rootAuthSession.getAuthenticationSession(client1, tabID.get());
testAuthenticationSession(authSession, client1.getId(), null, "foo"); testAuthenticationSession(authSession, client1.getId(), null, "foo");
assertThat(rootAuthSession.getTimestamp(), is(100)); assertThat(rootAuthSession.getTimestamp(), is(timestamp));
// Update and commit // Update and commit
authSession.setAction("foo-updated"); authSession.setAction("foo-updated");
rootAuthSession.setTimestamp(200); rootAuthSession.setTimestamp(timestamp + 1000);
authSession.setAuthenticatedUser(currentSession.users().getUserByUsername(realm, "user1")); authSession.setAuthenticatedUser(currentSession.users().getUserByUsername(realm, "user1"));
}); });
@ -141,7 +142,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
testAuthenticationSession(authSession, client1.getId(), user1.getId(), "foo-updated"); testAuthenticationSession(authSession, client1.getId(), user1.getId(), "foo-updated");
assertThat(rootAuthSession.getTimestamp(), is(200)); assertThat(rootAuthSession.getTimestamp(), is(timestamp + 1000));
// Remove and commit // Remove and commit
currentSession.authenticationSessions().removeRootAuthenticationSession(realm, rootAuthSession); currentSession.authenticationSessions().removeRootAuthenticationSession(realm, rootAuthSession);
@ -161,6 +162,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
public void testAuthenticationSessionRestart(KeycloakSession session) { public void testAuthenticationSessionRestart(KeycloakSession session) {
AtomicReference<String> parentAuthSessionID = new AtomicReference<>(); AtomicReference<String> parentAuthSessionID = new AtomicReference<>();
AtomicReference<String> tabID = new AtomicReference<>(); AtomicReference<String> tabID = new AtomicReference<>();
final int timestamp = Time.currentTime();
KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRestart1) -> { KeycloakModelUtils.runJobInTransaction(session.getKeycloakSessionFactory(), (KeycloakSession sessionRestart1) -> {
KeycloakSession currentSession = sessionRestart1; KeycloakSession currentSession = sessionRestart1;
@ -176,7 +178,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
tabID.set(authSession.getTabId()); tabID.set(authSession.getTabId());
authSession.setAction("foo"); authSession.setAction("foo");
authSession.getParentSession().setTimestamp(100); authSession.getParentSession().setTimestamp(timestamp);
authSession.setAuthenticatedUser(user1); authSession.setAuthenticatedUser(user1);
authSession.setAuthNote("foo", "bar"); authSession.setAuthNote("foo", "bar");
@ -256,7 +258,7 @@ public class AuthenticationSessionProviderTest extends AbstractTestRealmKeycloak
RealmModel realm = currentSession.realms().getRealm("test"); RealmModel realm = currentSession.realms().getRealm("test");
RealmModel fooRealm = currentSession.realms().createRealm("foo-realm"); RealmModel fooRealm = currentSession.realms().createRealm("foo-realm");
fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName())); fooRealm.setDefaultRole(currentSession.roles().addRealmRole(fooRealm, Constants.DEFAULT_ROLES_ROLE_PREFIX + "-" + fooRealm.getName()));
fooRealm.setAccessCodeLifespanLogin(1800);
fooRealm.addClient("foo-client"); fooRealm.addClient("foo-client");
authSessionID.set(currentSession.authenticationSessions().createRootAuthenticationSession(realm).getId()); authSessionID.set(currentSession.authenticationSessions().createRootAuthenticationSession(realm).getId());

View file

@ -179,8 +179,6 @@ public class AuthenticationSessionTest extends KeycloakModelTest {
Assert.assertNotNull(rootAuthSession); Assert.assertNotNull(rootAuthSession);
Time.setOffset(1900); Time.setOffset(1900);
// not needed with Infinispan where expiration handles Infinispan itself
session.authenticationSessions().removeExpired(realm);
return null; return null;
}); });