fix: limit the use of Resteasy to the KeycloakSession (#28150)
* fix: limit the use of Resteasy to the KeycloakSession contextualizes other state to the KeycloakSession close: #28152
This commit is contained in:
parent
fa1571f231
commit
be32f8b1bf
24 changed files with 147 additions and 374 deletions
|
@ -17,16 +17,13 @@
|
||||||
|
|
||||||
package org.keycloak.common.util;
|
package org.keycloak.common.util;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.ServiceLoader;
|
import java.util.ServiceLoader;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>Provides a layer of indirection to abstract invocations to Resteasy internal APIs. Making also possible to use different
|
* <p>Provides a layer of indirection to abstract invocations to Resteasy internal APIs for obtaining the KeycloakSession
|
||||||
* versions of Resteasy (e.g.: v3 and v4) depending on the stack that the server is running.
|
|
||||||
*
|
|
||||||
* <p>The methods herein provided are basically related with accessing context data from Resteasy, which changed in latest versions of Resteasy.
|
|
||||||
*
|
|
||||||
* <p>It is important to use this class when access to context data is necessary in order to avoid incompatibilities with future
|
|
||||||
* versions of Resteasy.
|
|
||||||
*
|
*
|
||||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||||
*/
|
*/
|
||||||
|
@ -35,38 +32,55 @@ public final class Resteasy {
|
||||||
private static ResteasyProvider provider;
|
private static ResteasyProvider provider;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
provider = ServiceLoader.load(ResteasyProvider.class, Resteasy.class.getClassLoader()).iterator().next();
|
Iterator<ResteasyProvider> iter = ServiceLoader.load(ResteasyProvider.class, Resteasy.class.getClassLoader()).iterator();
|
||||||
|
if (iter.hasNext()) {
|
||||||
|
provider = iter.next();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final ThreadLocal<Map<Class<?>, Object>> contextualData = new ThreadLocal<Map<Class<?>, Object>>() {
|
||||||
|
@Override
|
||||||
|
protected Map<Class<?>, Object> initialValue() {
|
||||||
|
return new HashMap<>(1);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
public static ResteasyProvider getProvider() {
|
public static ResteasyProvider getProvider() {
|
||||||
return provider;
|
return provider;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Push the given {@code instance} with type/key {@code type} to the Resteasy context associated with the current thread.
|
* Push the given {@code instance} with type/key {@code type} to the Resteasy context associated with the current thread.
|
||||||
*
|
* <br>Should not be called directly
|
||||||
* @param type the type/key to associate the {@code instance} with
|
*
|
||||||
|
* @param type the type/key to associate the {@code instance} with
|
||||||
* @param instance the instance
|
* @param instance the instance
|
||||||
*/
|
*/
|
||||||
public static void pushContext(Class type, Object instance) {
|
public static <R> R pushContext(Class<R> type, R instance) {
|
||||||
provider.pushContext(type, instance);
|
return (R) contextualData.get().put(type, instance);
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Lookup the instance associated with the given type/key {@code type} from the Resteasy context associated with the current thread.
|
|
||||||
*
|
|
||||||
* @param type the type/key to lookup
|
|
||||||
* @return the instance associated with the given {@code type} or null if non-existent.
|
|
||||||
*/
|
|
||||||
public static <R> R getContextData(Class<R> type) {
|
|
||||||
return provider.getContextData(type);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear the Resteasy context associated with the current thread.
|
* Clear the Resteasy context associated with the current thread.
|
||||||
|
* <br>Should not be called directly
|
||||||
*/
|
*/
|
||||||
public static void clearContextData() {
|
public static void clearContextData() {
|
||||||
provider.clearContextData();
|
contextualData.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Lookup the instance associated with the given type/key {@code type} from the Resteasy context associated with the current thread, or from the provider.
|
||||||
|
* <br> Should only be used to obtain the KeycloakSession
|
||||||
|
*
|
||||||
|
* @param type the type/key to lookup
|
||||||
|
* @return the instance associated with the given {@code type} or null if non-existent.
|
||||||
|
*/
|
||||||
|
public static <R> R getContextData(Class<R> type) {
|
||||||
|
R result = (R) contextualData.get().get(type);
|
||||||
|
if (result != null) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
return provider.getContextData(type);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -74,9 +88,11 @@ public final class Resteasy {
|
||||||
*
|
*
|
||||||
* @param type the type/key to associate the {@code instance} with
|
* @param type the type/key to associate the {@code instance} with
|
||||||
* @param instance the instance
|
* @param instance the instance
|
||||||
|
* @deprecated use {@link #pushContext(Class, Object)}
|
||||||
*/
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void pushDefaultContextObject(Class type, Object instance) {
|
public static void pushDefaultContextObject(Class type, Object instance) {
|
||||||
provider.pushDefaultContextObject(type, instance);
|
pushContext(type, instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,4 @@ public interface ResteasyProvider {
|
||||||
|
|
||||||
<R> R getContextData(Class<R> type);
|
<R> R getContextData(Class<R> type);
|
||||||
|
|
||||||
void pushDefaultContextObject(Class type, Object instance);
|
|
||||||
|
|
||||||
void pushContext(Class type, Object instance);
|
|
||||||
|
|
||||||
void clearContextData();
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,6 +24,8 @@ import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
|
||||||
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
|
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
|
||||||
import org.keycloak.common.util.Resteasy;
|
import org.keycloak.common.util.Resteasy;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
|
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||||
import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler;
|
import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler;
|
||||||
|
|
||||||
import io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext;
|
import io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext;
|
||||||
|
@ -42,8 +44,9 @@ public final class CreateSessionHandler implements ServerRestHandler, Transactio
|
||||||
// make sure the session is created once
|
// make sure the session is created once
|
||||||
KeycloakSession session = create();
|
KeycloakSession session = create();
|
||||||
routingContext.put(KeycloakSession.class.getName(), session);
|
routingContext.put(KeycloakSession.class.getName(), session);
|
||||||
|
// the CloseSessionFilter is needed because it runs sooner than this callback
|
||||||
|
// this is just a catch-all if the CloseSessionFilter doesn't get a chance to run
|
||||||
context.registerCompletionCallback(this);
|
context.registerCompletionCallback(this);
|
||||||
Resteasy.pushContext(KeycloakSession.class, session);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,6 @@ import io.vertx.core.http.HttpServerRequest;
|
||||||
import org.jboss.resteasy.reactive.server.core.CurrentRequestManager;
|
import org.jboss.resteasy.reactive.server.core.CurrentRequestManager;
|
||||||
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
|
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.http.HttpRequest;
|
import org.keycloak.http.HttpRequest;
|
||||||
import org.keycloak.http.HttpResponse;
|
import org.keycloak.http.HttpResponse;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -29,8 +28,6 @@ import org.keycloak.services.DefaultKeycloakContext;
|
||||||
|
|
||||||
public final class QuarkusKeycloakContext extends DefaultKeycloakContext {
|
public final class QuarkusKeycloakContext extends DefaultKeycloakContext {
|
||||||
|
|
||||||
private ClientConnection clientConnection;
|
|
||||||
|
|
||||||
public QuarkusKeycloakContext(KeycloakSession session) {
|
public QuarkusKeycloakContext(KeycloakSession session) {
|
||||||
super(session);
|
super(session);
|
||||||
}
|
}
|
||||||
|
@ -46,22 +43,10 @@ public final class QuarkusKeycloakContext extends DefaultKeycloakContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientConnection getConnection() {
|
protected ClientConnection createClientConnection() {
|
||||||
if (clientConnection == null) {
|
ResteasyReactiveRequestContext requestContext = getResteasyReactiveRequestContext();
|
||||||
ClientConnection contextualObject = Resteasy.getContextData(ClientConnection.class);
|
HttpServerRequest serverRequest = requestContext.unwrap(HttpServerRequest.class);
|
||||||
|
return new QuarkusClientConnection(serverRequest);
|
||||||
if (contextualObject == null) {
|
|
||||||
ResteasyReactiveRequestContext requestContext = getResteasyReactiveRequestContext();
|
|
||||||
HttpServerRequest serverRequest = requestContext.unwrap(HttpServerRequest.class);
|
|
||||||
clientConnection = new QuarkusClientConnection(serverRequest);
|
|
||||||
} else {
|
|
||||||
// in case the request is dispatched to a different thread like when using JAX-RS async responses
|
|
||||||
// in this case, we expect the client connection available as a contextual data
|
|
||||||
clientConnection = contextualObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return clientConnection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private ResteasyReactiveRequestContext getResteasyReactiveRequestContext() {
|
private ResteasyReactiveRequestContext getResteasyReactiveRequestContext() {
|
||||||
|
|
|
@ -22,51 +22,24 @@ import io.quarkus.vertx.http.runtime.CurrentVertxRequest;
|
||||||
import io.vertx.ext.web.RoutingContext;
|
import io.vertx.ext.web.RoutingContext;
|
||||||
import org.keycloak.common.util.ResteasyProvider;
|
import org.keycloak.common.util.ResteasyProvider;
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
import jakarta.enterprise.context.ContextNotActiveException;
|
||||||
|
|
||||||
public class ResteasyVertxProvider implements ResteasyProvider {
|
public class ResteasyVertxProvider implements ResteasyProvider {
|
||||||
|
|
||||||
private static final ThreadLocal<Map<Class<?>, Object>> contextualData = new ThreadLocal<Map<Class<?>, Object>>() {
|
|
||||||
@Override
|
|
||||||
protected Map<Class<?>, Object> initialValue() {
|
|
||||||
return new HashMap<>();
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <R> R getContextData(Class<R> type) {
|
public <R> R getContextData(Class<R> type) {
|
||||||
R data = (R) contextualData.get().get(type);
|
return (R) getRoutingContext().map(c -> c.get(type.getName())).orElse(null);
|
||||||
|
}
|
||||||
|
|
||||||
if (data == null) {
|
private static Optional<RoutingContext> getRoutingContext() {
|
||||||
RoutingContext contextData = Optional.ofNullable(Arc.container())
|
try {
|
||||||
.map(c -> c.instance(CurrentVertxRequest.class).get()).map(CurrentVertxRequest::getCurrent)
|
return Optional.ofNullable(Arc.container())
|
||||||
.orElse(null);
|
.map(c -> c.instance(CurrentVertxRequest.class).get()).map(CurrentVertxRequest::getCurrent);
|
||||||
|
} catch (ContextNotActiveException e) {
|
||||||
if (contextData == null) {
|
return Optional.empty();
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (R) contextData.data().get(type.getName());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return data;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushContext(Class type, Object instance) {
|
|
||||||
contextualData.get().put(type, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushDefaultContextObject(Class type, Object instance) {
|
|
||||||
pushContext(type, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearContextData() {
|
|
||||||
contextualData.remove();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
||||||
import org.keycloak.common.util.CertificateUtils;
|
import org.keycloak.common.util.CertificateUtils;
|
||||||
import org.keycloak.common.util.KeyUtils;
|
import org.keycloak.common.util.KeyUtils;
|
||||||
import org.keycloak.common.util.PemUtils;
|
import org.keycloak.common.util.PemUtils;
|
||||||
|
import org.keycloak.common.util.Resteasy;
|
||||||
import org.keycloak.common.util.SecretGenerator;
|
import org.keycloak.common.util.SecretGenerator;
|
||||||
import org.keycloak.common.util.Time;
|
import org.keycloak.common.util.Time;
|
||||||
import org.keycloak.component.ComponentModel;
|
import org.keycloak.component.ComponentModel;
|
||||||
|
@ -375,12 +376,15 @@ public final class KeycloakModelUtils {
|
||||||
V result;
|
V result;
|
||||||
try (KeycloakSession session = factory.create()) {
|
try (KeycloakSession session = factory.create()) {
|
||||||
session.getTransactionManager().begin();
|
session.getTransactionManager().begin();
|
||||||
|
KeycloakSession old = Resteasy.pushContext(KeycloakSession.class, session);
|
||||||
try {
|
try {
|
||||||
cloneContextRealmClientToSession(context, session);
|
cloneContextRealmClientToSession(context, session);
|
||||||
result = callable.run(session);
|
result = callable.run(session);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
session.getTransactionManager().setRollbackOnly();
|
session.getTransactionManager().setRollbackOnly();
|
||||||
throw t;
|
throw t;
|
||||||
|
} finally {
|
||||||
|
Resteasy.pushContext(KeycloakSession.class, old);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -55,7 +55,15 @@ public interface KeycloakContext {
|
||||||
|
|
||||||
HttpHeaders getRequestHeaders();
|
HttpHeaders getRequestHeaders();
|
||||||
|
|
||||||
<T> T getContextObject(Class<T> clazz);
|
/**
|
||||||
|
* Will always return null. You should not need access to a general context object.
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
@Deprecated(forRemoval = true)
|
||||||
|
default <T> T getContextObject(Class<T> clazz) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
RealmModel getRealm();
|
RealmModel getRealm();
|
||||||
|
|
||||||
|
@ -68,17 +76,23 @@ public interface KeycloakContext {
|
||||||
ClientConnection getConnection();
|
ClientConnection getConnection();
|
||||||
|
|
||||||
Locale resolveLocale(UserModel user);
|
Locale resolveLocale(UserModel user);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get current AuthenticationSessionModel, can be null out of the AuthenticationSession context.
|
* Get current AuthenticationSessionModel, can be null out of the AuthenticationSession context.
|
||||||
*
|
*
|
||||||
* @return current AuthenticationSessionModel or null
|
* @return current AuthenticationSessionModel or null
|
||||||
*/
|
*/
|
||||||
AuthenticationSessionModel getAuthenticationSession();
|
AuthenticationSessionModel getAuthenticationSession();
|
||||||
|
|
||||||
void setAuthenticationSession(AuthenticationSessionModel authenticationSession);
|
void setAuthenticationSession(AuthenticationSessionModel authenticationSession);
|
||||||
|
|
||||||
HttpRequest getHttpRequest();
|
HttpRequest getHttpRequest();
|
||||||
|
|
||||||
HttpResponse getHttpResponse();
|
HttpResponse getHttpResponse();
|
||||||
|
|
||||||
|
void setConnection(ClientConnection clientConnection);
|
||||||
|
|
||||||
|
void setHttpRequest(HttpRequest httpRequest);
|
||||||
|
|
||||||
|
void setHttpResponse(HttpResponse httpResponse);
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,6 @@ package org.keycloak.events.email;
|
||||||
import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
|
import static org.keycloak.models.utils.KeycloakModelUtils.runJobInTransaction;
|
||||||
|
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.email.EmailException;
|
import org.keycloak.email.EmailException;
|
||||||
import org.keycloak.email.EmailTemplateProvider;
|
import org.keycloak.email.EmailTemplateProvider;
|
||||||
import org.keycloak.events.Event;
|
import org.keycloak.events.Event;
|
||||||
|
@ -88,7 +87,7 @@ public class EmailEventListenerProvider implements EventListenerProvider {
|
||||||
context.setClient(client);
|
context.setClient(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
Resteasy.pushContext(HttpRequest.class, request);
|
context.setHttpRequest(request);
|
||||||
|
|
||||||
UserModel user = session.users().getUserById(realm, event.getUserId());
|
UserModel user = session.users().getUserById(realm, event.getUserId());
|
||||||
|
|
||||||
|
|
|
@ -27,7 +27,6 @@ import org.keycloak.broker.saml.SAMLDataMarshaller;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.VerificationException;
|
import org.keycloak.common.VerificationException;
|
||||||
import org.keycloak.common.util.PemUtils;
|
import org.keycloak.common.util.PemUtils;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.connections.httpclient.HttpClientProvider;
|
import org.keycloak.connections.httpclient.HttpClientProvider;
|
||||||
import org.keycloak.crypto.Algorithm;
|
import org.keycloak.crypto.Algorithm;
|
||||||
import org.keycloak.crypto.KeyStatus;
|
import org.keycloak.crypto.KeyStatus;
|
||||||
|
@ -56,7 +55,6 @@ import org.keycloak.models.AuthenticatedClientSessionModel;
|
||||||
import org.keycloak.models.ClientModel;
|
import org.keycloak.models.ClientModel;
|
||||||
import org.keycloak.models.KeyManager;
|
import org.keycloak.models.KeyManager;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakTransaction;
|
|
||||||
import org.keycloak.models.KeycloakUriInfo;
|
import org.keycloak.models.KeycloakUriInfo;
|
||||||
import org.keycloak.models.RealmModel;
|
import org.keycloak.models.RealmModel;
|
||||||
import org.keycloak.models.SingleUseObjectProvider;
|
import org.keycloak.models.SingleUseObjectProvider;
|
||||||
|
@ -197,7 +195,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isDestinationRequired() {
|
protected boolean isDestinationRequired() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -404,7 +402,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
|
|
||||||
ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("saml-artifact-pool");
|
ExecutorService executor = session.getProvider(ExecutorsProvider.class).getExecutor("saml-artifact-pool");
|
||||||
|
|
||||||
ArtifactResolutionRunnable artifactResolutionRunnable = new ArtifactResolutionRunnable(getBindingType(), asyncResponse, doc, clientArtifactBindingURI, relayState, session.getContext().getConnection());
|
ArtifactResolutionRunnable artifactResolutionRunnable = new ArtifactResolutionRunnable(getBindingType(), asyncResponse, doc, clientArtifactBindingURI, relayState);
|
||||||
ScheduledTaskRunner task = new ScheduledTaskRunner(session.getKeycloakSessionFactory(), artifactResolutionRunnable);
|
ScheduledTaskRunner task = new ScheduledTaskRunner(session.getKeycloakSessionFactory(), artifactResolutionRunnable);
|
||||||
executor.execute(task);
|
executor.execute(task);
|
||||||
|
|
||||||
|
@ -935,7 +933,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
RealmsResource.protocolUrl(uriInfo).path(SamlService.ARTIFACT_RESOLUTION_SERVICE_PATH)
|
RealmsResource.protocolUrl(uriInfo).path(SamlService.ARTIFACT_RESOLUTION_SERVICE_PATH)
|
||||||
.build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL),
|
.build(realm.getName(), SamlProtocol.LOGIN_PROTOCOL),
|
||||||
RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(),
|
RealmsResource.realmBaseUrl(uriInfo).build(realm.getName()).toString(),
|
||||||
true,
|
true,
|
||||||
signingKeys);
|
signingKeys);
|
||||||
} catch (Exception ex) {
|
} catch (Exception ex) {
|
||||||
logger.error("Cannot generate IdP metadata", ex);
|
logger.error("Cannot generate IdP metadata", ex);
|
||||||
|
@ -1085,7 +1083,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
"ArtifactResolve message: %s", DocumentUtil.asString(soapBodyContents));
|
"ArtifactResolve message: %s", DocumentUtil.asString(soapBodyContents));
|
||||||
return Soap.createFault().reason("").detail("").build();
|
return Soap.createFault().reason("").detail("").build();
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return artifactResolve(artifactResolveType, samlDocumentHolder);
|
return artifactResolve(artifactResolveType, samlDocumentHolder);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
@ -1168,7 +1166,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
logger.errorf("Artifact to resolve was null");
|
logger.errorf("Artifact to resolve was null");
|
||||||
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
|
return emptyArtifactResponseMessage(artifactResolveMessage, null, JBossSAMLURIConstants.STATUS_REQUEST_DENIED.getUri());
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtifactResolver artifactResolver = getArtifactResolver(artifact);
|
ArtifactResolver artifactResolver = getArtifactResolver(artifact);
|
||||||
|
|
||||||
if (artifactResolver == null) {
|
if (artifactResolver == null) {
|
||||||
|
@ -1252,7 +1250,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
|
|
||||||
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
|
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response emptyArtifactResponseMessage(ArtifactResolveType artifactResolveMessage, ClientModel clientModel) throws ProcessingException, ConfigurationException {
|
private Response emptyArtifactResponseMessage(ArtifactResolveType artifactResolveMessage, ClientModel clientModel) throws ProcessingException, ConfigurationException {
|
||||||
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel, JBossSAMLURIConstants.STATUS_SUCCESS.getUri());
|
return emptyArtifactResponseMessage(artifactResolveMessage, clientModel, JBossSAMLURIConstants.STATUS_SUCCESS.getUri());
|
||||||
}
|
}
|
||||||
|
@ -1271,7 +1269,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
|
|
||||||
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
|
return artifactResponseMessage(artifactResolveMessage, artifactResponseDocument, clientModel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Response artifactResponseMessage(ArtifactResolveType artifactResolveMessage, Document artifactResponseDocument, ClientModel clientModel) throws ProcessingException, ConfigurationException {
|
private Response artifactResponseMessage(ArtifactResolveType artifactResolveMessage, Document artifactResponseDocument, ClientModel clientModel) throws ProcessingException, ConfigurationException {
|
||||||
// Add "inResponseTo" to artifactResponse
|
// Add "inResponseTo" to artifactResponse
|
||||||
if (artifactResolveMessage.getID() != null && !artifactResolveMessage.getID().trim().isEmpty()){
|
if (artifactResolveMessage.getID() != null && !artifactResolveMessage.getID().trim().isEmpty()){
|
||||||
|
@ -1279,7 +1277,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
artifactResponseElement.setAttribute("InResponseTo", artifactResolveMessage.getID());
|
artifactResponseElement.setAttribute("InResponseTo", artifactResolveMessage.getID());
|
||||||
}
|
}
|
||||||
JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(session);
|
JaxrsSAML2BindingBuilder bindingBuilder = new JaxrsSAML2BindingBuilder(session);
|
||||||
|
|
||||||
if (clientModel != null) {
|
if (clientModel != null) {
|
||||||
SamlClient samlClient = new SamlClient(clientModel);
|
SamlClient samlClient = new SamlClient(clientModel);
|
||||||
|
|
||||||
|
@ -1360,36 +1358,28 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
private URI clientArtifactBindingURI;
|
private URI clientArtifactBindingURI;
|
||||||
private String relayState;
|
private String relayState;
|
||||||
private Document doc;
|
private Document doc;
|
||||||
private UriInfo uri;
|
|
||||||
private String realmId;
|
private String realmId;
|
||||||
private ClientConnection connection;
|
private ClientConnection connection;
|
||||||
private String bindingType;
|
private String bindingType;
|
||||||
|
|
||||||
public ArtifactResolutionRunnable(String bindingType, AsyncResponse asyncResponse, Document doc, URI clientArtifactBindingURI, String relayState, ClientConnection connection){
|
public ArtifactResolutionRunnable(String bindingType, AsyncResponse asyncResponse, Document doc, URI clientArtifactBindingURI, String relayState){
|
||||||
this.asyncResponse = asyncResponse;
|
this.asyncResponse = asyncResponse;
|
||||||
this.doc = doc;
|
this.doc = doc;
|
||||||
this.clientArtifactBindingURI = clientArtifactBindingURI;
|
this.clientArtifactBindingURI = clientArtifactBindingURI;
|
||||||
this.relayState = relayState;
|
this.relayState = relayState;
|
||||||
this.uri = session.getContext().getUri();
|
|
||||||
this.realmId = realm.getId();
|
this.realmId = realm.getId();
|
||||||
this.connection = connection;
|
this.connection = session.getContext().getConnection();
|
||||||
this.bindingType = bindingType;
|
this.bindingType = bindingType;
|
||||||
this.request = session.getContext().getHttpRequest();
|
this.request = session.getContext().getHttpRequest();
|
||||||
this.response = session.getContext().getHttpResponse();
|
this.response = session.getContext().getHttpResponse();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public void run(KeycloakSession session){
|
public void run(KeycloakSession session){
|
||||||
// Initialize context
|
// Initialize context
|
||||||
Resteasy.pushContext(UriInfo.class, uri);
|
session.getContext().setHttpRequest(request);
|
||||||
|
session.getContext().setHttpResponse(response);
|
||||||
KeycloakTransaction tx = session.getTransactionManager();
|
session.getContext().setConnection(connection);
|
||||||
Resteasy.pushContext(KeycloakTransaction.class, tx);
|
|
||||||
|
|
||||||
Resteasy.pushContext(KeycloakSession.class, session);
|
|
||||||
Resteasy.pushContext(HttpRequest.class, request);
|
|
||||||
Resteasy.pushContext(HttpResponse.class, response);
|
|
||||||
Resteasy.pushContext(ClientConnection.class, connection);
|
|
||||||
|
|
||||||
RealmManager realmManager = new RealmManager(session);
|
RealmManager realmManager = new RealmManager(session);
|
||||||
RealmModel realm = realmManager.getRealm(realmId);
|
RealmModel realm = realmManager.getRealm(realmId);
|
||||||
|
@ -1424,7 +1414,7 @@ public class SamlService extends AuthorizationEndpointBase {
|
||||||
if (logger.isTraceEnabled()) {
|
if (logger.isTraceEnabled()) {
|
||||||
logger.tracef("Resolved object: %s" + DocumentUtil.asString(samlDoc.getSamlDocument()));
|
logger.tracef("Resolved object: %s" + DocumentUtil.asString(samlDoc.getSamlDocument()));
|
||||||
}
|
}
|
||||||
|
|
||||||
ArtifactResponseType art = (ArtifactResponseType) samlDoc.getSamlObject();
|
ArtifactResponseType art = (ArtifactResponseType) samlDoc.getSamlObject();
|
||||||
|
|
||||||
if (art.getAny() == null) {
|
if (art.getAny() == null) {
|
||||||
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.services;
|
||||||
|
|
||||||
import jakarta.ws.rs.core.HttpHeaders;
|
import jakarta.ws.rs.core.HttpHeaders;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.http.HttpRequest;
|
import org.keycloak.http.HttpRequest;
|
||||||
import org.keycloak.http.HttpResponse;
|
import org.keycloak.http.HttpResponse;
|
||||||
import org.keycloak.locale.LocaleSelectorProvider;
|
import org.keycloak.locale.LocaleSelectorProvider;
|
||||||
|
@ -53,6 +52,7 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
private AuthenticationSessionModel authenticationSession;
|
private AuthenticationSessionModel authenticationSession;
|
||||||
private HttpRequest request;
|
private HttpRequest request;
|
||||||
private HttpResponse response;
|
private HttpResponse response;
|
||||||
|
private ClientConnection clientConnection;
|
||||||
|
|
||||||
public DefaultKeycloakContext(KeycloakSession session) {
|
public DefaultKeycloakContext(KeycloakSession session) {
|
||||||
this.session = session;
|
this.session = session;
|
||||||
|
@ -96,11 +96,6 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
return getHttpRequest().getHttpHeaders();
|
return getHttpRequest().getHttpHeaders();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public <T> T getContextObject(Class<T> clazz) {
|
|
||||||
return Resteasy.getContextData(clazz);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public RealmModel getRealm() {
|
public RealmModel getRealm() {
|
||||||
return realm;
|
return realm;
|
||||||
|
@ -124,7 +119,15 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public ClientConnection getConnection() {
|
public ClientConnection getConnection() {
|
||||||
return getContextObject(ClientConnection.class);
|
if (clientConnection == null) {
|
||||||
|
synchronized (this) {
|
||||||
|
if (clientConnection == null) {
|
||||||
|
clientConnection = createClientConnection();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientConnection;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -146,7 +149,6 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
public HttpRequest getHttpRequest() {
|
public HttpRequest getHttpRequest() {
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
request = getContextObject(HttpRequest.class);
|
|
||||||
if (request == null) {
|
if (request == null) {
|
||||||
request = createHttpRequest();
|
request = createHttpRequest();
|
||||||
}
|
}
|
||||||
|
@ -160,7 +162,6 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
public HttpResponse getHttpResponse() {
|
public HttpResponse getHttpResponse() {
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
synchronized (this) {
|
synchronized (this) {
|
||||||
response = getContextObject(HttpResponse.class);
|
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
response = createHttpResponse();
|
response = createHttpResponse();
|
||||||
}
|
}
|
||||||
|
@ -170,6 +171,10 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected ClientConnection createClientConnection() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract HttpRequest createHttpRequest();
|
protected abstract HttpRequest createHttpRequest();
|
||||||
|
|
||||||
protected abstract HttpResponse createHttpResponse();
|
protected abstract HttpResponse createHttpResponse();
|
||||||
|
@ -177,4 +182,20 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
||||||
protected KeycloakSession getSession() {
|
protected KeycloakSession getSession() {
|
||||||
return session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setConnection(ClientConnection clientConnection) {
|
||||||
|
this.clientConnection = clientConnection;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHttpRequest(HttpRequest httpRequest) {
|
||||||
|
this.request = httpRequest;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setHttpResponse(HttpResponse httpResponse) {
|
||||||
|
this.response = httpResponse;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,71 +0,0 @@
|
||||||
package org.keycloak.services.filters;
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Copyright 2020 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.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
|
|
||||||
import org.keycloak.common.ClientConnection;
|
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.models.KeycloakSession;
|
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
|
||||||
import org.keycloak.models.KeycloakTransactionManager;
|
|
||||||
import org.keycloak.services.resources.KeycloakApplication;
|
|
||||||
|
|
||||||
|
|
||||||
public abstract class AbstractRequestFilter {
|
|
||||||
|
|
||||||
protected void filter(ClientConnection clientConnection, Consumer<KeycloakSession> next) {
|
|
||||||
KeycloakSessionFactory sessionFactory = getSessionFactory();
|
|
||||||
KeycloakSession session = sessionFactory.create();
|
|
||||||
|
|
||||||
KeycloakTransactionManager tx = session.getTransactionManager();
|
|
||||||
tx.begin();
|
|
||||||
|
|
||||||
try {
|
|
||||||
Resteasy.pushContext(ClientConnection.class, clientConnection);
|
|
||||||
Resteasy.pushContext(KeycloakSession.class, session);
|
|
||||||
|
|
||||||
next.accept(session);
|
|
||||||
} catch (Exception e) {
|
|
||||||
tx.setRollbackOnly();
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
} finally {
|
|
||||||
if (isAutoClose()) {
|
|
||||||
close(session);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected KeycloakSessionFactory getSessionFactory() {
|
|
||||||
return KeycloakApplication.getSessionFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void close(KeycloakSession session) {
|
|
||||||
session.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Indicates whether or not resources should be close as part of the execution of the {@link #filter(ClientConnection, Consumer)}
|
|
||||||
* method.
|
|
||||||
*
|
|
||||||
* @return true if resources should be close automatically. Otherwise, false.
|
|
||||||
*/
|
|
||||||
protected boolean isAutoClose() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -19,7 +19,6 @@ package org.keycloak.services.resources;
|
||||||
import org.jboss.logging.Logger;
|
import org.jboss.logging.Logger;
|
||||||
import org.keycloak.Config;
|
import org.keycloak.Config;
|
||||||
import org.keycloak.common.crypto.CryptoIntegration;
|
import org.keycloak.common.crypto.CryptoIntegration;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.config.ConfigProviderFactory;
|
import org.keycloak.config.ConfigProviderFactory;
|
||||||
import org.keycloak.exportimport.ExportImportConfig;
|
import org.keycloak.exportimport.ExportImportConfig;
|
||||||
import org.keycloak.exportimport.ExportImportManager;
|
import org.keycloak.exportimport.ExportImportManager;
|
||||||
|
@ -76,7 +75,6 @@ public abstract class KeycloakApplication extends Application {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
logger.debugv("PlatformProvider: {0}", platform.getClass().getName());
|
logger.debugv("PlatformProvider: {0}", platform.getClass().getName());
|
||||||
logger.debugv("RestEasy provider: {0}", Resteasy.getProvider().getClass().getName());
|
|
||||||
|
|
||||||
loadConfig();
|
loadConfig();
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import java.net.URI;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
import org.jboss.resteasy.core.ResteasyContext;
|
|
||||||
import org.jboss.resteasy.mock.MockHttpRequest;
|
import org.jboss.resteasy.mock.MockHttpRequest;
|
||||||
import org.junit.Assert;
|
import org.junit.Assert;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
@ -46,12 +45,12 @@ public class RedirectUtilsTest {
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void beforeClass() {
|
public static void beforeClass() {
|
||||||
HttpRequest httpRequest = new HttpRequestImpl(MockHttpRequest.create("GET", URI.create("https://keycloak.org/"), URI.create("https://keycloak.org")));
|
HttpRequest httpRequest = new HttpRequestImpl(MockHttpRequest.create("GET", URI.create("https://keycloak.org/"), URI.create("https://keycloak.org")));
|
||||||
ResteasyContext.getContextDataMap().put(HttpRequest.class, httpRequest);
|
|
||||||
Profile.defaults();
|
Profile.defaults();
|
||||||
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
|
CryptoIntegration.init(CryptoProvider.class.getClassLoader());
|
||||||
ResteasyKeycloakSessionFactory sessionFactory = new ResteasyKeycloakSessionFactory();
|
ResteasyKeycloakSessionFactory sessionFactory = new ResteasyKeycloakSessionFactory();
|
||||||
sessionFactory.init();
|
sessionFactory.init();
|
||||||
session = new ResteasyKeycloakSession(sessionFactory);
|
session = new ResteasyKeycloakSession(sessionFactory);
|
||||||
|
session.getContext().setHttpRequest(httpRequest);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2023 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.protocol.oidc.utils;
|
|
||||||
|
|
||||||
import org.jboss.resteasy.core.ResteasyContext;
|
|
||||||
import org.keycloak.common.util.ResteasyProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* <p>Resteasy provider to be used for the utils class.</p>
|
|
||||||
* @author rmartinc
|
|
||||||
*/
|
|
||||||
public class ResteasyTestProvider implements ResteasyProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> R getContextData(Class<R> type) {
|
|
||||||
return ResteasyContext.getContextData(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushDefaultContextObject(Class type, Object instance) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushContext(Class type, Object instance) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearContextData() {
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -31,9 +31,10 @@ import jakarta.ws.rs.core.MultivaluedMap;
|
||||||
import jakarta.ws.rs.core.UriInfo;
|
import jakarta.ws.rs.core.UriInfo;
|
||||||
import jakarta.ws.rs.ext.MessageBodyReader;
|
import jakarta.ws.rs.ext.MessageBodyReader;
|
||||||
import jakarta.ws.rs.ext.Providers;
|
import jakarta.ws.rs.ext.Providers;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.core.ResteasyContext;
|
||||||
import org.jboss.resteasy.reactive.server.multipart.FormValue;
|
import org.jboss.resteasy.reactive.server.multipart.FormValue;
|
||||||
import org.jboss.resteasy.reactive.server.multipart.MultipartFormDataInput;
|
import org.jboss.resteasy.reactive.server.multipart.MultipartFormDataInput;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
import org.keycloak.http.FormPartValue;
|
import org.keycloak.http.FormPartValue;
|
||||||
import org.keycloak.http.HttpRequest;
|
import org.keycloak.http.HttpRequest;
|
||||||
import org.keycloak.services.FormPartValueImpl;
|
import org.keycloak.services.FormPartValueImpl;
|
||||||
|
@ -75,7 +76,7 @@ public class HttpRequestImpl implements HttpRequest {
|
||||||
return new MultivaluedHashMap<>();
|
return new MultivaluedHashMap<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
Providers providers = Resteasy.getContextData(Providers.class);
|
Providers providers = ResteasyContext.getContextData(Providers.class);
|
||||||
MessageBodyReader<MultipartFormDataInput> multiPartProvider = providers.getMessageBodyReader(
|
MessageBodyReader<MultipartFormDataInput> multiPartProvider = providers.getMessageBodyReader(
|
||||||
MultipartFormDataInput.class, null, null, MULTIPART_FORM_DATA_TYPE);
|
MultipartFormDataInput.class, null, null, MULTIPART_FORM_DATA_TYPE);
|
||||||
MultipartFormDataInput inputs = multiPartProvider
|
MultipartFormDataInput inputs = multiPartProvider
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
|
|
||||||
package org.keycloak.services.resteasy;
|
package org.keycloak.services.resteasy;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.core.ResteasyContext;
|
||||||
import org.keycloak.http.HttpRequest;
|
import org.keycloak.http.HttpRequest;
|
||||||
import org.keycloak.http.HttpResponse;
|
import org.keycloak.http.HttpResponse;
|
||||||
import org.keycloak.models.KeycloakSession;
|
import org.keycloak.models.KeycloakSession;
|
||||||
|
@ -30,12 +31,12 @@ public class ResteasyKeycloakContext extends DefaultKeycloakContext {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpRequest createHttpRequest() {
|
protected HttpRequest createHttpRequest() {
|
||||||
return new HttpRequestImpl(getContextObject(org.jboss.resteasy.spi.HttpRequest.class));
|
return new HttpRequestImpl(ResteasyContext.getContextData(org.jboss.resteasy.spi.HttpRequest.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected HttpResponse createHttpResponse() {
|
protected HttpResponse createHttpResponse() {
|
||||||
return new HttpResponseImpl(getContextObject(org.jboss.resteasy.spi.HttpResponse.class));
|
return new HttpResponseImpl(ResteasyContext.getContextData(org.jboss.resteasy.spi.HttpResponse.class));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,17 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright 2023 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.
|
|
||||||
|
|
||||||
org.keycloak.protocol.oidc.utils.ResteasyTestProvider
|
|
|
@ -44,7 +44,7 @@ import static org.keycloak.testsuite.admin.AbstractAdminTest.loadJson;
|
||||||
*/
|
*/
|
||||||
public abstract class AbstractTestRealmKeycloakTest extends AbstractKeycloakTest {
|
public abstract class AbstractTestRealmKeycloakTest extends AbstractKeycloakTest {
|
||||||
public static final String TEST_REALM_NAME = "test";
|
public static final String TEST_REALM_NAME = "test";
|
||||||
|
|
||||||
protected RealmResource testRealm() {
|
protected RealmResource testRealm() {
|
||||||
return adminClient.realm(TEST_REALM_NAME);
|
return adminClient.realm(TEST_REALM_NAME);
|
||||||
}
|
}
|
||||||
|
@ -128,7 +128,7 @@ public abstract class AbstractTestRealmKeycloakTest extends AbstractKeycloakTest
|
||||||
|
|
||||||
/** KEYCLOAK-12065 Inherit Client Connection from parent session **/
|
/** KEYCLOAK-12065 Inherit Client Connection from parent session **/
|
||||||
public static KeycloakSession inheritClientConnection(KeycloakSession parentSession, KeycloakSession currentSession) {
|
public static KeycloakSession inheritClientConnection(KeycloakSession parentSession, KeycloakSession currentSession) {
|
||||||
Resteasy.pushContext(ClientConnection.class, parentSession.getContext().getConnection());
|
currentSession.getContext().setConnection(parentSession.getContext().getConnection());
|
||||||
return currentSession;
|
return currentSession;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,45 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright 2021 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.testsuite.model;
|
|
||||||
|
|
||||||
import org.keycloak.common.util.ResteasyProvider;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @author <a href="mailto:mkanis@redhat.com">Martin Kanis</a>
|
|
||||||
*/
|
|
||||||
public class ResteasyNullProvider implements ResteasyProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> R getContextData(Class<R> type) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushDefaultContextObject(Class type, Object instance) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushContext(Class type, Object instance) {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearContextData() {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
#
|
|
||||||
# Copyright 2021 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.
|
|
||||||
|
|
||||||
org.keycloak.testsuite.model.ResteasyNullProvider
|
|
|
@ -23,7 +23,8 @@ import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import jakarta.servlet.ServletContext;
|
import jakarta.servlet.ServletContext;
|
||||||
import org.keycloak.common.util.Resteasy;
|
|
||||||
|
import org.jboss.resteasy.core.ResteasyContext;
|
||||||
import org.keycloak.common.util.SystemEnvProperties;
|
import org.keycloak.common.util.SystemEnvProperties;
|
||||||
import org.keycloak.util.JsonSerialization;
|
import org.keycloak.util.JsonSerialization;
|
||||||
|
|
||||||
|
@ -38,7 +39,7 @@ public class JsonConfigProviderFactory extends org.keycloak.services.util.JsonCo
|
||||||
|
|
||||||
private Map<String, String> getPropertyOverrides() {
|
private Map<String, String> getPropertyOverrides() {
|
||||||
|
|
||||||
ServletContext context = Resteasy.getContextData(ServletContext.class);
|
ServletContext context = ResteasyContext.getContextData(ServletContext.class);
|
||||||
Map<String, String> propertyOverridesMap = new HashMap<>();
|
Map<String, String> propertyOverridesMap = new HashMap<>();
|
||||||
String propertyOverrides = context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES);
|
String propertyOverrides = context.getInitParameter(SERVER_CONTEXT_CONFIG_PROPERTY_OVERRIDES);
|
||||||
|
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
package org.keycloak.testsuite;
|
|
||||||
|
|
||||||
import org.jboss.resteasy.core.ResteasyContext;
|
|
||||||
import org.jboss.resteasy.spi.ResteasyProviderFactory;
|
|
||||||
import org.keycloak.common.util.ResteasyProvider;
|
|
||||||
|
|
||||||
public class Resteasy4Provider implements ResteasyProvider {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public <R> R getContextData(Class<R> type) {
|
|
||||||
return ResteasyProviderFactory.getInstance().getContextData(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushDefaultContextObject(Class type, Object instance) {
|
|
||||||
ResteasyProviderFactory.getInstance().getContextData(org.jboss.resteasy.spi.Dispatcher.class).getDefaultContextObjects()
|
|
||||||
.put(type, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void pushContext(Class type, Object instance) {
|
|
||||||
ResteasyContext.pushContext(type, instance);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearContextData() {
|
|
||||||
ResteasyContext.clearContextData();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -26,11 +26,13 @@ import jakarta.servlet.ServletRequest;
|
||||||
import jakarta.servlet.ServletResponse;
|
import jakarta.servlet.ServletResponse;
|
||||||
import jakarta.servlet.http.HttpServletRequest;
|
import jakarta.servlet.http.HttpServletRequest;
|
||||||
|
|
||||||
|
import org.jboss.resteasy.core.ResteasyContext;
|
||||||
import org.keycloak.common.ClientConnection;
|
import org.keycloak.common.ClientConnection;
|
||||||
|
import org.keycloak.models.KeycloakSession;
|
||||||
import org.keycloak.models.KeycloakSessionFactory;
|
import org.keycloak.models.KeycloakSessionFactory;
|
||||||
import org.keycloak.services.filters.AbstractRequestFilter;
|
import org.keycloak.models.utils.KeycloakModelUtils;
|
||||||
|
|
||||||
public class UndertowRequestFilter extends AbstractRequestFilter implements Filter {
|
public class UndertowRequestFilter implements Filter {
|
||||||
|
|
||||||
private final KeycloakSessionFactory factory;
|
private final KeycloakSessionFactory factory;
|
||||||
|
|
||||||
|
@ -44,8 +46,11 @@ public class UndertowRequestFilter extends AbstractRequestFilter implements Filt
|
||||||
servletRequest.setCharacterEncoding("UTF-8");
|
servletRequest.setCharacterEncoding("UTF-8");
|
||||||
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
final HttpServletRequest request = (HttpServletRequest) servletRequest;
|
||||||
|
|
||||||
filter(createClientConnection(request), (session) -> {
|
ClientConnection connection = createClientConnection(request);
|
||||||
|
KeycloakModelUtils.runJobInTransaction(factory, session -> {
|
||||||
try {
|
try {
|
||||||
|
ResteasyContext.pushContext(KeycloakSession.class, session);
|
||||||
|
session.getContext().setConnection(connection);
|
||||||
filterChain.doFilter(servletRequest, servletResponse);
|
filterChain.doFilter(servletRequest, servletResponse);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
|
@ -88,11 +93,6 @@ public class UndertowRequestFilter extends AbstractRequestFilter implements Filt
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected KeycloakSessionFactory getSessionFactory() {
|
|
||||||
return this.factory;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void init(FilterConfig filterConfig) {
|
public void init(FilterConfig filterConfig) {
|
||||||
}
|
}
|
||||||
|
|
|
@ -1 +0,0 @@
|
||||||
org.keycloak.testsuite.Resteasy4Provider
|
|
Loading…
Reference in a new issue