Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,6 @@
import org.keycloak.common.enums.HostnameVerificationPolicy;
import org.keycloak.common.profile.PropertiesProfileConfigResolver;
import org.keycloak.common.util.HtmlUtils;
import org.keycloak.common.util.Time;
import org.keycloak.component.ComponentModel;
import org.keycloak.events.Event;
import org.keycloak.events.EventListenerProvider;
Expand All @@ -72,7 +71,6 @@
import org.keycloak.models.UserSessionModel;
import org.keycloak.models.session.UserSessionPersisterProvider;
import org.keycloak.models.utils.ModelToRepresentation;
import org.keycloak.models.utils.ResetTimeOffsetEvent;
import org.keycloak.protocol.oid4vc.issuance.OID4VCIssuerWellKnownProvider;
import org.keycloak.protocol.oid4vc.issuance.credentialoffer.CredentialOfferState;
import org.keycloak.protocol.oid4vc.issuance.credentialoffer.CredentialOfferStorage;
Expand Down Expand Up @@ -187,33 +185,6 @@ public Integer getClientSessionsCountInUserSession(@QueryParam("realm") final St
return sessionModel.getAuthenticatedClientSessions().size();
}

@GET
@Path("/time-offset")
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> getTimeOffset() {
Map<String, String> response = new HashMap<>();
response.put("currentTime", String.valueOf(Time.currentTime()));
response.put("offset", String.valueOf(Time.getOffset()));
return response;
}

@PUT
@Path("/time-offset")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> setTimeOffset(Map<String, String> time) {
int offset = Integer.parseInt(time.get("offset"));

Time.setOffset(offset);

// Time offset was restarted
if (offset == 0) {
session.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
}

return getTimeOffset();
}

@POST
@Path("/poll-event-queue")
@Produces(MediaType.APPLICATION_JSON)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,6 @@
@Consumes(MediaType.APPLICATION_JSON)
public interface TestingResource {

@GET
@Path("/time-offset")
@Produces(MediaType.APPLICATION_JSON)
Map<String, String> getTimeOffset();

@PUT
@Path("/time-offset")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
Map<String, String> setTimeOffset(Map<String, String> time);

@POST
@Path("/poll-event-queue")
@Produces(MediaType.APPLICATION_JSON)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package org.keycloak.testframework.remote.timeoffset;

import org.keycloak.common.util.Time;
import org.keycloak.models.utils.ResetTimeOffsetEvent;
import org.keycloak.testsuite.AbstractKeycloakTest;

public class TimeOffSet {

private final AbstractKeycloakTest test;

public TimeOffSet(AbstractKeycloakTest test) {
this.test = test;
}

public void set(int offset) {
test.shouldResetTimeOffset(offset != 0);

// adminClient depends on Time.offset for auto-refreshing tokens
Time.setOffset(offset);
test.getTestingClient().server().run(
session -> {
Time.setOffset(offset);

// Time offset was restarted
if (offset == 0) {
session.getKeycloakSessionFactory().publish(new ResetTimeOffsetEvent());
}
}
);

// force getting new token after time offset has changed
test.getAdminClient().tokenManager().grantToken();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
import org.keycloak.representations.idm.RealmRepresentation;
import org.keycloak.representations.idm.RequiredActionProviderRepresentation;
import org.keycloak.representations.idm.UserRepresentation;
import org.keycloak.testframework.remote.timeoffset.TimeOffSet;
import org.keycloak.testsuite.admin.AdminApiUtil;
import org.keycloak.testsuite.arquillian.KcArquillian;
import org.keycloak.testsuite.arquillian.SuiteContext;
Expand Down Expand Up @@ -139,6 +140,8 @@ public abstract class AbstractKeycloakTest {

protected KeycloakTestingClient.Server runOnServer;

protected TimeOffSet timeOffSet = new TimeOffSet(this);

@ArquillianResource
protected OAuthClient oauth;

Expand Down Expand Up @@ -237,7 +240,7 @@ protected void postAfterAbstractKeycloak() throws Exception {
@After
public void afterAbstractKeycloakTest() throws Exception {
if (resetTimeOffset) {
resetTimeOffset();
timeOffSet.set(0);
}

if (isImportAfterEachMethod()) {
Expand Down Expand Up @@ -674,29 +677,15 @@ public void setTimeOfDay(int hour, int minute, int second, int addSeconds) {
now.set(Calendar.SECOND, second);
int offset = (int) ((now.getTime().getTime() - System.currentTimeMillis()) / 1000);

setTimeOffset(offset + addSeconds);
}

/**
* Sets time offset in seconds that will be added to Time.currentTime() and Time.currentTimeMillis() both for client and server.
* Moves time on the remote Infinispan server as well if the HotRod storage is used.
*
* @param offset
*/
public void setTimeOffset(int offset) {
String response = invokeTimeOffset(offset);
resetTimeOffset = offset != 0;
log.debugv("Set time offset, response {0}", response);
timeOffSet.set(offset + addSeconds);
}

public void resetTimeOffset() {
String response = invokeTimeOffset(0);
resetTimeOffset = false;
log.debugv("Reset time offset, response {0}", response);
public void shouldResetTimeOffset(boolean resetTimeOffset) {
this.resetTimeOffset = resetTimeOffset;
}

public void setOtpTimeOffset(int offsetSeconds, TimeBasedOTP otp) {
setTimeOffset(offsetSeconds);
timeOffSet.set(offsetSeconds);
final Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.SECOND, offsetSeconds);
otp.setCalendar(calendar);
Expand All @@ -706,18 +695,6 @@ public int getCurrentTime() {
return Time.currentTime();
}

protected String invokeTimeOffset(int offset) {
// adminClient depends on Time.offset for auto-refreshing tokens
Time.setOffset(offset);
Map result = testingClient.testing().setTimeOffset(Collections.singletonMap("offset", String.valueOf(offset)));

// force getting new token after time offset has changed
adminClient.tokenManager().grantToken();


return String.valueOf(result);
}

private void loadConstantsProperties() throws ConfigurationException {
constantsProperties = new PropertiesConfiguration(System.getProperty("testsuite.constants"));
constantsProperties.setThrowExceptionOnMissing(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ public void resetPasswordRequiresReAuth() {

EventAssertion.expectLoginSuccess(events.poll());

setTimeOffset(350);
timeOffSet.set(350);

// Should prompt for re-authentication
doAIA();
Expand Down Expand Up @@ -236,7 +236,7 @@ public void resetPasswordRequiresReAuthWithIndividualMaxAuthAgeConfig() throws E

EventAssertion.expectLoginSuccess(events.poll());

setTimeOffset(550);
timeOffSet.set(550);

// Should prompt for re-authentication
doAIA();
Expand Down Expand Up @@ -275,7 +275,7 @@ public void resetPasswordRequiresNoReAuthWithIndividualMaxAuthAgeConfig() throws

EventAssertion.expectLoginSuccess(events.poll());

setTimeOffset(350);
timeOffSet.set(350);

// Should not prompt for re-authentication
doAIA();
Expand Down Expand Up @@ -313,7 +313,7 @@ public void resetPasswordRequiresReAuthWithMaxAuthAgePasswordPolicy() {
EventAssertion.expectLoginSuccess(events.poll());

// we need to add some slack to avoid timing issues
setTimeOffset(1);
timeOffSet.set(1);

// Should prompt for re-authentication due to maxAuthAge password policy
doAIA();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public class AppInitiatedActionUpdateEmailTest extends AbstractAppInitiatedActio

@After
public void after() {
setTimeOffset(0);
timeOffSet.set(0);
// update email required action max auth age back to default
Optional<RequiredActionProviderRepresentation> updateEmailAction = managedRealm.admin().flows().getRequiredActions()
.stream()
Expand Down Expand Up @@ -145,7 +145,7 @@ public void updateEmailReAuthentication() {
appPage.openAccount();
loginPage.login("test-user@localhost", "password");

setTimeOffset(400);
timeOffSet.set(400);
UIUtils.clickLink(updateEmailBtn);
loginPage.assertCurrent();
loginPage.login("password");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -456,11 +456,11 @@ public void verifyEmailResendTooFast() {
Assertions.assertEquals(1, greenMail.getReceivedMessages().length);

try {
setTimeOffset(40);
timeOffSet.set(40);
verifyEmailPage.clickResendEmail();
Assertions.assertEquals(2, greenMail.getReceivedMessages().length);
} finally {
setTimeOffset(0);
timeOffSet.set(0);
}

}
Expand Down Expand Up @@ -681,7 +681,7 @@ public void verifyEmailExpiredCode() throws IOException {
events.poll();

try {
setTimeOffset(360);
timeOffSet.set(360);

driver.navigate().to(verificationUrl.trim());

Expand All @@ -697,7 +697,7 @@ public void verifyEmailExpiredCode() throws IOException {
.detail(Details.ACTION, VerifyEmailActionToken.TOKEN_TYPE)
.assertEvent();
} finally {
setTimeOffset(0);
timeOffSet.set(0);
}
}

Expand All @@ -723,7 +723,7 @@ public void verifyEmailExpiredCodedPerActionLifespan() throws IOException {
events.poll();

try {
setTimeOffset(70);
timeOffSet.set(70);

driver.navigate().to(verificationUrl.trim());

Expand All @@ -739,7 +739,7 @@ public void verifyEmailExpiredCodedPerActionLifespan() throws IOException {
.detail(Details.ACTION, VerifyEmailActionToken.TOKEN_TYPE)
.assertEvent();
} finally {
setTimeOffset(0);
timeOffSet.set(0);
realmRep.setAttributes(originalAttributes);
managedRealm.admin().update(realmRep);
}
Expand Down Expand Up @@ -768,7 +768,7 @@ public void verifyEmailExpiredCodedPerActionMultipleTimeouts() throws IOExceptio
events.poll();

try {
setTimeOffset(70);
timeOffSet.set(70);

driver.navigate().to(verificationUrl.trim());

Expand All @@ -784,7 +784,7 @@ public void verifyEmailExpiredCodedPerActionMultipleTimeouts() throws IOExceptio
.detail(Details.ACTION, VerifyEmailActionToken.TOKEN_TYPE)
.assertEvent();
} finally {
setTimeOffset(0);
timeOffSet.set(0);
realmRep.setAttributes(originalAttributes);
managedRealm.admin().update(realmRep);
}
Expand All @@ -806,7 +806,7 @@ public void verifyEmailExpiredCodeAndExpiredSession() throws IOException {
events.poll();

try {
setTimeOffset(3600);
timeOffSet.set(3600);

driver.manage().deleteAllCookies();

Expand All @@ -824,7 +824,7 @@ public void verifyEmailExpiredCodeAndExpiredSession() throws IOException {
.detail(Details.ACTION, VerifyEmailActionToken.TOKEN_TYPE)
.assertEvent();
} finally {
setTimeOffset(0);
timeOffSet.set(0);
}
}

Expand Down Expand Up @@ -1191,14 +1191,14 @@ public void verifyEmailExpiredRegistration() throws IOException {
String verificationUrl = getEmailLink(message);

try {
setTimeOffset(360);
timeOffSet.set(360);

driver.navigate().to(verificationUrl.trim());

loginPage.assertCurrent();
assertEquals("Action expired. Please start again.", loginPage.getError());
} finally {
setTimeOffset(0);
timeOffSet.set(0);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ public void resetPasswordActionNotTriggered() {

try {
RealmManager.realm(managedRealm.admin()).passwordPolicy("forceExpiredPasswordChange(1)");
setTimeOffset(60 * 60 * 48);
timeOffSet.set(60 * 60 * 48);

//create username only flow
testingClient.server("test").run(session -> FlowUtil.inCurrentRealm(session).copyBrowserFlow(newFlowAlias));
Expand All @@ -239,7 +239,7 @@ public void resetPasswordActionNotTriggered() {
.ifPresent(authenticationFlowRepresentation ->
managedRealm.admin().flows().deleteFlow(authenticationFlowRepresentation.getId()));

setTimeOffset(0);
timeOffSet.set(0);
RealmManager.realm(managedRealm.admin()).passwordPolicy(null);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -703,7 +703,7 @@ public void testUpdateEmailVerificationResendTooFast() throws Exception {

try {
// Move time forward beyond cooldown period (default 30 seconds)
setTimeOffset(40);
timeOffSet.set(40);

// Logout and login again to retry after cooldown
managedRealm.admin().users().get(testUser.getId()).logout();
Expand All @@ -715,7 +715,7 @@ public void testUpdateEmailVerificationResendTooFast() throws Exception {
updateEmailPage.changeEmail("newemail@localhost");
assertEquals(2, greenMail.getReceivedMessages().length, "Second email should be sent after cooldown expires");
} finally {
setTimeOffset(0);
timeOffSet.set(0);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ public void setDefaultPageUriParameters() {
}

protected void setAdapterAndServerTimeOffset(int timeOffset, String... servletUris) {
setTimeOffset(timeOffset);
timeOffSet.set(timeOffset);

for (String servletUri : servletUris) {
setAdapterServletTimeOffset(timeOffset, servletUri);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ public void addAdapterTestRealms(List<RealmRepresentation> testRealms) {
}

protected void setAdapterAndServerTimeOffset(int timeOffset, String... servletUris) {
setTimeOffset(timeOffset);
timeOffSet.set(timeOffset);

Arrays.stream(servletUris)
.map(url -> url += "unsecured")
Expand Down
Loading
Loading