mirror of
https://github.com/keycloak/keycloak.git
synced 2026-01-10 15:32:05 -03:30
Support setting time offset on test server within tests (#35495)
Closes: #34189 Signed-off-by: Simon Vacek <simonvacky@email.cz>
This commit is contained in:
parent
806e140f45
commit
9abbf3edc6
@ -87,6 +87,18 @@
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-remote</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-remote-providers</artifactId>
|
||||
<version>${project.version}</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</dependencyManagement>
|
||||
</project>
|
||||
|
||||
@ -21,6 +21,14 @@ public class KeycloakUrls {
|
||||
return toUrl(getBase());
|
||||
}
|
||||
|
||||
public String getMasterRealm() {
|
||||
return baseUrl + "/realms/master";
|
||||
}
|
||||
|
||||
public URL getMasterRealmUrl() {
|
||||
return toUrl(getMasterRealm());
|
||||
}
|
||||
|
||||
public String getAdmin() {
|
||||
return baseUrl + "/admin";
|
||||
}
|
||||
|
||||
@ -80,6 +80,10 @@
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-ui</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-remote</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-example-providers</artifactId>
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package org.keycloak.test.examples;
|
||||
|
||||
import org.junit.jupiter.api.Assertions;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.keycloak.test.framework.annotations.KeycloakIntegrationTest;
|
||||
import org.keycloak.test.framework.remote.timeoffset.InjectTimeOffSet;
|
||||
import org.keycloak.test.framework.remote.timeoffset.TimeOffSet;
|
||||
|
||||
@KeycloakIntegrationTest
|
||||
public class TimeOffSetExampleTest {
|
||||
|
||||
@InjectTimeOffSet(offset = 3)
|
||||
TimeOffSet timeOffSet;
|
||||
|
||||
@Test
|
||||
public void testSetOffset() {
|
||||
int offset = timeOffSet.get();
|
||||
Assertions.assertEquals(3, offset);
|
||||
Assertions.assertDoesNotThrow(() -> timeOffSet.set(10));
|
||||
offset = timeOffSet.get();
|
||||
Assertions.assertEquals(10, offset);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetOffset() {
|
||||
int offset = timeOffSet.get();
|
||||
Assertions.assertEquals(3, offset);
|
||||
}
|
||||
}
|
||||
@ -41,9 +41,11 @@
|
||||
<module>db-oracle</module>
|
||||
<module>db-postgres</module>
|
||||
<module>email-server</module>
|
||||
<module>oauth-nimbus-poc</module>
|
||||
<module>ui</module>
|
||||
<module>examples</module>
|
||||
<module>oauth-nimbus-poc</module>
|
||||
<module>remote</module>
|
||||
<module>remote-providers</module>
|
||||
<module>ui</module>
|
||||
</modules>
|
||||
|
||||
</project>
|
||||
|
||||
56
test-framework/remote-providers/pom.xml
Normal file
56
test-framework/remote-providers/pom.xml
Normal file
@ -0,0 +1,56 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-test-framework-parent</artifactId>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<version>999.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-test-framework-remote-providers</artifactId>
|
||||
<name>Keycloak Test Framework - Remote providers</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Utility Keycloak server Providers for the Keycloak Test Framework</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-core</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak</groupId>
|
||||
<artifactId>keycloak-server-spi-private</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.ws.rs</groupId>
|
||||
<artifactId>jakarta.ws.rs-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,55 @@
|
||||
package org.keycloak.test.framework.remote.providers;
|
||||
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.services.resource.RealmResourceProvider;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class TimeOffSetRealmResourceProvider implements RealmResourceProvider {
|
||||
|
||||
private final KeycloakSession session;
|
||||
private final String KEY_OFFSET = "offset";
|
||||
|
||||
public TimeOffSetRealmResourceProvider(KeycloakSession session) {
|
||||
this.session = session;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getResource() {
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response getTimeOffset() {
|
||||
int offset = Time.getOffset();
|
||||
var time = Map.of(KEY_OFFSET, offset);
|
||||
return Response.ok(time).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
public Response setTimeOffset(Map<String, Integer> time) {
|
||||
if (!time.containsKey(KEY_OFFSET)) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).build();
|
||||
}
|
||||
Time.setOffset(time.get(KEY_OFFSET));
|
||||
return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON).build();
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package org.keycloak.test.framework.remote.providers;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.services.resource.RealmResourceProvider;
|
||||
import org.keycloak.services.resource.RealmResourceProviderFactory;
|
||||
|
||||
public class TimeOffSetRealmResourceProviderFactory implements RealmResourceProviderFactory {
|
||||
|
||||
private final String ID = "testing-timeoffset";
|
||||
|
||||
@Override
|
||||
public RealmResourceProvider create(KeycloakSession session) {
|
||||
return new TimeOffSetRealmResourceProvider(session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void postInit(KeycloakSessionFactory factory) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(org.keycloak.Config.Scope config) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.keycloak.test.framework.remote.providers.TimeOffSetRealmResourceProviderFactory
|
||||
47
test-framework/remote/pom.xml
Normal file
47
test-framework/remote/pom.xml
Normal file
@ -0,0 +1,47 @@
|
||||
<?xml version="1.0"?>
|
||||
<!--
|
||||
~ Copyright 2016 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.
|
||||
-->
|
||||
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<parent>
|
||||
<artifactId>keycloak-test-framework-parent</artifactId>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<version>999.0.0-SNAPSHOT</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<artifactId>keycloak-test-framework-remote</artifactId>
|
||||
<name>Keycloak Test Framework - Remote testing utils</name>
|
||||
<packaging>jar</packaging>
|
||||
<description>Utility Keycloak server Suppliers for the Keycloak Test Framework</description>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-core</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.keycloak.test</groupId>
|
||||
<artifactId>keycloak-test-framework-remote-providers</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
@ -0,0 +1,14 @@
|
||||
package org.keycloak.test.framework.remote;
|
||||
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
public @interface InjectRemoteProviders {
|
||||
|
||||
LifeCycle lifecycle() default LifeCycle.GLOBAL;
|
||||
}
|
||||
@ -0,0 +1,4 @@
|
||||
package org.keycloak.test.framework.remote;
|
||||
|
||||
public class RemoteProviders {
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package org.keycloak.test.framework.remote;
|
||||
|
||||
import org.keycloak.test.framework.annotations.InjectTestDatabase;
|
||||
import org.keycloak.test.framework.database.TestDatabase;
|
||||
import org.keycloak.test.framework.injection.InstanceContext;
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
import org.keycloak.test.framework.injection.RequestedInstance;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
import org.keycloak.test.framework.injection.SupplierOrder;
|
||||
import org.keycloak.test.framework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.test.framework.server.KeycloakServerConfigInterceptor;
|
||||
|
||||
public class RemoteProvidersSupplier implements Supplier<RemoteProviders, InjectRemoteProviders>, KeycloakServerConfigInterceptor<TestDatabase, InjectTestDatabase> {
|
||||
@Override
|
||||
public Class<InjectRemoteProviders> getAnnotationClass() {
|
||||
return InjectRemoteProviders.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<RemoteProviders> getValueType() {
|
||||
return RemoteProviders.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RemoteProviders getValue(InstanceContext<RemoteProviders, InjectRemoteProviders> instanceContext) {
|
||||
return new RemoteProviders();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifeCycle getDefaultLifecycle() {
|
||||
return LifeCycle.GLOBAL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<RemoteProviders, InjectRemoteProviders> a, RequestedInstance<RemoteProviders, InjectRemoteProviders> b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return SupplierOrder.BEFORE_KEYCLOAK_SERVER;
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder intercept(KeycloakServerConfigBuilder serverConfig, InstanceContext<TestDatabase, InjectTestDatabase> instanceContext) {
|
||||
return serverConfig.dependency("org.keycloak.test", "keycloak-test-framework-remote-providers");
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package org.keycloak.test.framework.remote;
|
||||
|
||||
import org.keycloak.test.framework.TestFrameworkExtension;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
import org.keycloak.test.framework.remote.timeoffset.TimeOffsetSupplier;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class RemoteTestFrameworkExtension implements TestFrameworkExtension {
|
||||
@Override
|
||||
public List<Supplier<?, ?>> suppliers() {
|
||||
return List.of(
|
||||
new TimeOffsetSupplier(),
|
||||
new RemoteProvidersSupplier()
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,17 @@
|
||||
package org.keycloak.test.framework.remote.timeoffset;
|
||||
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface InjectTimeOffSet {
|
||||
|
||||
LifeCycle lifecycle() default LifeCycle.METHOD;
|
||||
|
||||
int offset() default 0;
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package org.keycloak.test.framework.remote.timeoffset;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.apache.http.HttpResponse;
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.apache.http.client.methods.HttpPut;
|
||||
import org.apache.http.entity.StringEntity;
|
||||
import org.keycloak.common.util.Time;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
|
||||
public class TimeOffSet {
|
||||
private int currentOffset;
|
||||
private final String KEY_OFFSET = "offset";
|
||||
private final String TIME_OFFSET_ENDPOINT = "/testing-timeoffset";
|
||||
private final HttpClient httpClient;
|
||||
private final String serverUrl;
|
||||
|
||||
public TimeOffSet(HttpClient httpClient, String serverUrl, int initOffset) {
|
||||
this.httpClient = httpClient;
|
||||
this.serverUrl = serverUrl;
|
||||
if (initOffset != 0) {
|
||||
set(initOffset);
|
||||
}
|
||||
currentOffset = initOffset;
|
||||
}
|
||||
|
||||
public void set(int offset) throws RuntimeException {
|
||||
currentOffset = offset;
|
||||
|
||||
// set for tests
|
||||
Time.setOffset(currentOffset);
|
||||
|
||||
// set for KC server
|
||||
var time = Map.of(KEY_OFFSET, currentOffset);
|
||||
try {
|
||||
ObjectMapper objectMapper = new ObjectMapper();
|
||||
String json = objectMapper.writeValueAsString(time);
|
||||
|
||||
HttpPut request = new HttpPut(serverUrl + TIME_OFFSET_ENDPOINT);
|
||||
request.setEntity(new StringEntity(json));
|
||||
request.setHeader("Content-type", "application/json");
|
||||
|
||||
HttpResponse response = httpClient.execute(request);
|
||||
if (response.getStatusLine().getStatusCode() != Response.Status.OK.getStatusCode()) {
|
||||
var statusLine = response.getStatusLine();
|
||||
throw new WebApplicationException(String.format("Unexpected response status for TimeOffSet: %d %s", statusLine.getStatusCode(), statusLine.getReasonPhrase()));
|
||||
}
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int get() {
|
||||
return currentOffset;
|
||||
}
|
||||
|
||||
public boolean hasChanged() {
|
||||
return currentOffset != 0;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package org.keycloak.test.framework.remote.timeoffset;
|
||||
|
||||
import org.apache.http.client.HttpClient;
|
||||
import org.keycloak.test.framework.injection.InstanceContext;
|
||||
import org.keycloak.test.framework.injection.LifeCycle;
|
||||
import org.keycloak.test.framework.injection.RequestedInstance;
|
||||
import org.keycloak.test.framework.injection.Supplier;
|
||||
import org.keycloak.test.framework.injection.SupplierOrder;
|
||||
import org.keycloak.test.framework.remote.RemoteProviders;
|
||||
import org.keycloak.test.framework.server.KeycloakServerConfigBuilder;
|
||||
import org.keycloak.test.framework.server.KeycloakServerConfigInterceptor;
|
||||
import org.keycloak.test.framework.server.KeycloakUrls;
|
||||
|
||||
public class TimeOffsetSupplier implements Supplier<TimeOffSet, InjectTimeOffSet>, KeycloakServerConfigInterceptor<TimeOffSet, InjectTimeOffSet> {
|
||||
@Override
|
||||
public Class<InjectTimeOffSet> getAnnotationClass() {
|
||||
return InjectTimeOffSet.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<TimeOffSet> getValueType() {
|
||||
return TimeOffSet.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimeOffSet getValue(InstanceContext<TimeOffSet, InjectTimeOffSet> instanceContext) {
|
||||
var httpClient = instanceContext.getDependency(HttpClient.class);
|
||||
var remoteProviders = instanceContext.getDependency(RemoteProviders.class);
|
||||
KeycloakUrls keycloakUrls = instanceContext.getDependency(KeycloakUrls.class);
|
||||
|
||||
int initOffset = instanceContext.getAnnotation().offset();
|
||||
return new TimeOffSet(httpClient, keycloakUrls.getMasterRealm(), initOffset);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean compatible(InstanceContext<TimeOffSet, InjectTimeOffSet> a, RequestedInstance<TimeOffSet, InjectTimeOffSet> b) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LifeCycle getDefaultLifecycle() {
|
||||
return LifeCycle.METHOD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close(InstanceContext<TimeOffSet, InjectTimeOffSet> instanceContext) {
|
||||
if (instanceContext.getLifeCycle() != LifeCycle.METHOD) {
|
||||
TimeOffSet timeOffSet = instanceContext.getValue();
|
||||
if (timeOffSet.hasChanged()) {
|
||||
timeOffSet.set(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int order() {
|
||||
return SupplierOrder.BEFORE_KEYCLOAK_SERVER;
|
||||
// Implementing the KeycloakServerConfigInterceptor is a workaround for RemoteProvidersSupplier to work
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeycloakServerConfigBuilder intercept(KeycloakServerConfigBuilder serverConfig, InstanceContext<TimeOffSet, InjectTimeOffSet> instanceContext) {
|
||||
return serverConfig;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1 @@
|
||||
org.keycloak.test.framework.remote.RemoteTestFrameworkExtension
|
||||
Loading…
x
Reference in New Issue
Block a user