further refinement of context handling (#28182)
* fully removing providers and moving the keycloaksession creation / final cleanup also deprecated Resteasy utility methods closes: #29223 Signed-off-by: Steve Hawkins <shawkins@redhat.com> Co-authored-by: Pedro Igor <pigor.craveiro@gmail.com>
This commit is contained in:
parent
3b1ca46be2
commit
4697cc956b
22 changed files with 163 additions and 289 deletions
|
@ -18,26 +18,18 @@
|
|||
package org.keycloak.common.util;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.ServiceLoader;
|
||||
|
||||
/**
|
||||
* <p>Provides a layer of indirection to abstract invocations to Resteasy internal APIs for obtaining the KeycloakSession
|
||||
* <p>Provides a way for obtaining the KeycloakSession
|
||||
*
|
||||
* @author <a href="mailto:psilva@redhat.com">Pedro Igor</a>
|
||||
*
|
||||
* @deprecated use org.keycloak.util.KeycloakSessionUtil instead
|
||||
*/
|
||||
@Deprecated
|
||||
public final class Resteasy {
|
||||
|
||||
private static ResteasyProvider provider;
|
||||
|
||||
static {
|
||||
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() {
|
||||
|
@ -45,12 +37,8 @@ public final class Resteasy {
|
|||
};
|
||||
};
|
||||
|
||||
public static ResteasyProvider getProvider() {
|
||||
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 context associated with the current thread.
|
||||
* <br>Should not be called directly
|
||||
*
|
||||
* @param type the type/key to associate the {@code instance} with
|
||||
|
@ -61,7 +49,7 @@ public final class Resteasy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Clear the Resteasy context associated with the current thread.
|
||||
* Clear the context associated with the current thread.
|
||||
* <br>Should not be called directly
|
||||
*/
|
||||
public static void clearContextData() {
|
||||
|
@ -69,18 +57,14 @@ public final class Resteasy {
|
|||
}
|
||||
|
||||
/**
|
||||
* Lookup the instance associated with the given type/key {@code type} from the Resteasy context associated with the current thread, or from the provider.
|
||||
* Lookup the instance associated with the given type/key {@code type} from the context associated with the current thread.
|
||||
* <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);
|
||||
return (R) contextualData.get().get(type);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -88,7 +72,6 @@ public final class Resteasy {
|
|||
*
|
||||
* @param type the type/key to associate the {@code instance} with
|
||||
* @param instance the instance
|
||||
* @deprecated use {@link #pushContext(Class, Object)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static void pushDefaultContextObject(Class type, Object instance) {
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
package org.keycloak.common.util;
|
||||
|
||||
public interface ResteasyProvider {
|
||||
|
||||
<R> R getContextData(Class<R> type);
|
||||
|
||||
}
|
|
@ -300,4 +300,10 @@ As a consequence of the removal of deprecated methods from `AccessToken`, `IDTok
|
|||
the `SingleUseObjectKeyModel` also changed to keep consistency with the method names related to expiration values.
|
||||
|
||||
The previous `getExpiration` method is now deprecated and you should prefer using new newly introduced `getExp` method
|
||||
to avoid overflow after 2038.
|
||||
to avoid overflow after 2038.
|
||||
|
||||
= Resteasy util class is deprecated
|
||||
|
||||
`org.keycloak.common.util.Resteasy` has been deprecated. You should use the `org.keycloak.util.KeycloakSessionUtil` to obtain the `KeycloakSession` instead.
|
||||
|
||||
It is highly recommended to avoid obtaining the `KeycloakSession` by means other than when creating your custom provider.
|
|
@ -40,7 +40,6 @@ import org.keycloak.Config;
|
|||
import org.keycloak.common.Profile;
|
||||
import org.keycloak.common.Profile.Feature;
|
||||
import org.keycloak.common.enums.SslRequired;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.config.HostnameV1Options;
|
||||
import org.keycloak.config.ProxyOptions;
|
||||
import org.keycloak.config.ProxyOptions.Mode;
|
||||
|
@ -50,6 +49,7 @@ import org.keycloak.provider.EnvironmentDependentProviderFactory;
|
|||
import org.keycloak.urls.HostnameProvider;
|
||||
import org.keycloak.urls.HostnameProviderFactory;
|
||||
import org.keycloak.urls.UrlType;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
public final class DefaultHostnameProvider implements HostnameProvider, HostnameProviderFactory, EnvironmentDependentProviderFactory {
|
||||
|
||||
|
@ -195,7 +195,7 @@ public final class DefaultHostnameProvider implements HostnameProvider, Hostname
|
|||
}
|
||||
|
||||
protected URI getRealmFrontEndUrl() {
|
||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
|
||||
if (session == null) {
|
||||
return null;
|
||||
|
|
|
@ -17,21 +17,27 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.integration.cdi;
|
||||
|
||||
import io.quarkus.arc.Unremovable;
|
||||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.enterprise.context.RequestScoped;
|
||||
import jakarta.enterprise.inject.Produces;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
import io.quarkus.arc.Unremovable;
|
||||
import jakarta.enterprise.inject.Disposes;
|
||||
|
||||
@ApplicationScoped
|
||||
@Unremovable
|
||||
public class KeycloakBeanProducer {
|
||||
public class KeycloakBeanProducer implements TransactionalSessionHandler {
|
||||
|
||||
@Produces
|
||||
@RequestScoped
|
||||
public KeycloakSession getKeycloakSession() {
|
||||
return Resteasy.getContextData(KeycloakSession.class);
|
||||
return create();
|
||||
}
|
||||
|
||||
void dispose(@Disposes KeycloakSession session) {
|
||||
KeycloakSessionUtil.setKeycloakSession(null);
|
||||
close(session);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,14 +28,12 @@ import jakarta.ws.rs.container.ContainerResponseFilter;
|
|||
import jakarta.ws.rs.container.PreMatching;
|
||||
import jakarta.ws.rs.core.StreamingOutput;
|
||||
import jakarta.ws.rs.ext.Provider;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
@Provider
|
||||
@PreMatching
|
||||
@Priority(1)
|
||||
public class CloseSessionHandler implements ContainerResponseFilter, TransactionalSessionHandler {
|
||||
public class CloseSessionFilter implements ContainerResponseFilter, org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler {
|
||||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext requestContext, ContainerResponseContext responseContext)
|
||||
|
@ -66,6 +64,6 @@ public class CloseSessionHandler implements ContainerResponseFilter, Transaction
|
|||
}
|
||||
|
||||
private void closeSession() {
|
||||
close(Resteasy.getContextData(KeycloakSession.class));
|
||||
close(KeycloakSessionUtil.getKeycloakSession());
|
||||
}
|
||||
}
|
|
@ -17,108 +17,13 @@
|
|||
|
||||
package org.keycloak.quarkus.runtime.integration.jaxrs;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import jakarta.ws.rs.core.MultivaluedMap;
|
||||
|
||||
public final class EmptyMultivaluedMap<K, V> implements MultivaluedMap<K, V> {
|
||||
import jakarta.ws.rs.core.AbstractMultivaluedMap;
|
||||
|
||||
@Override
|
||||
public void putSingle(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
public final class EmptyMultivaluedMap<K, V> extends AbstractMultivaluedMap<K, V> {
|
||||
|
||||
@Override
|
||||
public void add(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V getFirst(K key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(K key, V... newValues) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addAll(K key, List<V> valueList) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addFirst(K key, V value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equalsIgnoreValueOrder(MultivaluedMap<K, V> otherMap) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsKey(Object key) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean containsValue(Object value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> get(Object key) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> put(K key, List<V> value) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<V> remove(Object key) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void putAll(Map<? extends K, ? extends List<V>> m) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<K> keySet() {
|
||||
return emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Collection<List<V>> values() {
|
||||
return emptySet();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<K, List<V>>> entrySet() {
|
||||
return emptySet();
|
||||
public EmptyMultivaluedMap() {
|
||||
super(Map.of());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.quarkus.runtime.integration.resteasy;
|
||||
|
||||
import io.quarkus.resteasy.reactive.server.runtime.QuarkusResteasyReactiveRequestContext;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
|
||||
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
|
||||
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler;
|
||||
|
||||
import static org.keycloak.common.util.Resteasy.clearContextData;
|
||||
|
||||
public final class CreateSessionHandler implements ServerRestHandler, TransactionalSessionHandler {
|
||||
|
||||
@Override
|
||||
public void handle(ResteasyReactiveRequestContext requestContext) {
|
||||
QuarkusResteasyReactiveRequestContext context = (QuarkusResteasyReactiveRequestContext) requestContext;
|
||||
RoutingContext routingContext = context.getContext();
|
||||
KeycloakSession currentSession = routingContext.get(KeycloakSession.class.getName());
|
||||
|
||||
if (currentSession == null) {
|
||||
// this handler might be invoked multiple times when resolving sub-resources
|
||||
// make sure the session is created once
|
||||
KeycloakSession session = create();
|
||||
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(ignored -> {
|
||||
try {
|
||||
close(session);
|
||||
} catch (Exception e) {
|
||||
|
||||
}
|
||||
clearContextData();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -32,7 +32,7 @@ import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
|
|||
|
||||
public final class KeycloakHandlerChainCustomizer implements HandlerChainCustomizer {
|
||||
|
||||
private final CreateSessionHandler TRANSACTIONAL_SESSION_HANDLER = new CreateSessionHandler();
|
||||
private final TransactionalSessionHandler TRANSACTIONAL_SESSION_HANDLER = new TransactionalSessionHandler();
|
||||
|
||||
private final FormBodyHandler formBodyHandler = new FormBodyHandler(true, () -> Runnable::run, Set.of());
|
||||
|
||||
|
|
|
@ -1,45 +0,0 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.quarkus.runtime.integration.resteasy;
|
||||
|
||||
import io.quarkus.arc.Arc;
|
||||
import io.quarkus.vertx.http.runtime.CurrentVertxRequest;
|
||||
import io.vertx.ext.web.RoutingContext;
|
||||
import org.keycloak.common.util.ResteasyProvider;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import jakarta.enterprise.context.ContextNotActiveException;
|
||||
|
||||
public class ResteasyVertxProvider implements ResteasyProvider {
|
||||
|
||||
@Override
|
||||
public <R> R getContextData(Class<R> type) {
|
||||
return (R) getRoutingContext().map(c -> c.get(type.getName())).orElse(null);
|
||||
}
|
||||
|
||||
private static Optional<RoutingContext> getRoutingContext() {
|
||||
try {
|
||||
return Optional.ofNullable(Arc.container())
|
||||
.map(c -> c.instance(CurrentVertxRequest.class).get()).map(CurrentVertxRequest::getCurrent);
|
||||
} catch (ContextNotActiveException e) {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
/*
|
||||
* Copyright 2022 Red Hat, Inc. and/or its affiliates
|
||||
* and other contributors as indicated by the @author tags.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.keycloak.quarkus.runtime.integration.resteasy;
|
||||
|
||||
import io.quarkus.arc.Arc;
|
||||
|
||||
import org.jboss.resteasy.reactive.server.core.ResteasyReactiveRequestContext;
|
||||
import org.jboss.resteasy.reactive.server.spi.ServerRestHandler;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
public final class TransactionalSessionHandler implements ServerRestHandler, org.keycloak.quarkus.runtime.transaction.TransactionalSessionHandler {
|
||||
|
||||
@Override
|
||||
public void handle(ResteasyReactiveRequestContext requestContext) {
|
||||
requestContext.requireCDIRequestScope();
|
||||
KeycloakSession currentSession = Arc.container().instance(KeycloakSession.class).get();
|
||||
// this handler might be invoked multiple times when resolving sub-resources
|
||||
// make sure the transaction is began once when the session is first associated with the thread
|
||||
if (KeycloakSessionUtil.setKeycloakSession(currentSession) == null) {
|
||||
beginTransaction(currentSession);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -19,9 +19,7 @@ package org.keycloak.quarkus.runtime.transaction;
|
|||
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.models.KeycloakSessionFactory;
|
||||
import org.keycloak.models.KeycloakTransactionManager;
|
||||
import org.keycloak.quarkus.runtime.integration.QuarkusKeycloakSessionFactory;
|
||||
import org.keycloak.services.DefaultKeycloakSession;
|
||||
|
||||
/**
|
||||
* <p>A {@link TransactionalSessionHandler} is responsible for managing transaction sessions and its lifecycle. Its subtypes
|
||||
|
@ -31,24 +29,31 @@ import org.keycloak.services.DefaultKeycloakSession;
|
|||
public interface TransactionalSessionHandler {
|
||||
|
||||
/**
|
||||
* Creates a transactional {@link KeycloakSession}.
|
||||
* Creates a {@link KeycloakSession}.
|
||||
*
|
||||
* @return a transactional keycloak session
|
||||
* @return a keycloak session
|
||||
*/
|
||||
default KeycloakSession create() {
|
||||
KeycloakSessionFactory sessionFactory = QuarkusKeycloakSessionFactory.getInstance();
|
||||
KeycloakSession session = sessionFactory.create();
|
||||
session.getTransactionManager().begin();
|
||||
return session;
|
||||
return sessionFactory.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a transactional {@link KeycloakSession}.
|
||||
* begin a transaction if possible
|
||||
*
|
||||
* @param session a transactional session
|
||||
* @param session a session
|
||||
*/
|
||||
default void beginTransaction(KeycloakSession session) {
|
||||
session.getTransactionManager().begin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes a {@link KeycloakSession}.
|
||||
*
|
||||
* @param session a session
|
||||
*/
|
||||
default void close(KeycloakSession session) {
|
||||
if (session == null || DefaultKeycloakSession.class.cast(session).isClosed()) {
|
||||
if (session == null || session.isClosed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
org.keycloak.quarkus.runtime.integration.resteasy.ResteasyVertxProvider
|
|
@ -45,6 +45,6 @@ public class JaxRsDistTest {
|
|||
assertEquals(200, when().get("/").getStatusCode());
|
||||
|
||||
Awaitility.await().atMost(5, TimeUnit.SECONDS).untilAsserted(
|
||||
() -> cliResult.assertMessage("Request GET / has context request true has keycloaksession false"));
|
||||
() -> cliResult.assertMessage("Request GET / has context request true has keycloaksession true"));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import org.keycloak.broker.social.SocialIdentityProviderFactory;
|
|||
import org.keycloak.common.util.CertificateUtils;
|
||||
import org.keycloak.common.util.KeyUtils;
|
||||
import org.keycloak.common.util.PemUtils;
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.common.util.SecretGenerator;
|
||||
import org.keycloak.common.util.Time;
|
||||
import org.keycloak.component.ComponentModel;
|
||||
|
@ -53,6 +52,7 @@ import org.keycloak.models.UserModel;
|
|||
import org.keycloak.protocol.oidc.OIDCConfigAttributes;
|
||||
import org.keycloak.representations.idm.CertificateRepresentation;
|
||||
import org.keycloak.transaction.JtaTransactionManagerLookup;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import jakarta.transaction.InvalidTransactionException;
|
||||
|
@ -380,7 +380,7 @@ public final class KeycloakModelUtils {
|
|||
V result;
|
||||
try (KeycloakSession session = factory.create()) {
|
||||
session.getTransactionManager().begin();
|
||||
KeycloakSession old = Resteasy.pushContext(KeycloakSession.class, session);
|
||||
KeycloakSession old = KeycloakSessionUtil.setKeycloakSession(session);
|
||||
try {
|
||||
cloneContextRealmClientToSession(context, session);
|
||||
result = callable.run(session);
|
||||
|
@ -388,7 +388,7 @@ public final class KeycloakModelUtils {
|
|||
session.getTransactionManager().setRollbackOnly();
|
||||
throw t;
|
||||
} finally {
|
||||
Resteasy.pushContext(KeycloakSession.class, old);
|
||||
KeycloakSessionUtil.setKeycloakSession(old);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
|
|
@ -23,13 +23,13 @@ import java.util.function.BiConsumer;
|
|||
import jakarta.ws.rs.core.Response;
|
||||
import jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.http.HttpRequest;
|
||||
import org.keycloak.http.HttpResponse;
|
||||
import org.keycloak.models.ClientModel;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.provider.Provider;
|
||||
import org.keycloak.representations.AccessToken;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
/**
|
||||
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
|
||||
|
@ -54,12 +54,12 @@ public interface Cors extends Provider {
|
|||
public static final String INCLUDE_REDIRECTS = "+";
|
||||
|
||||
public static Cors add(HttpRequest request, ResponseBuilder response) {
|
||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
return session.getProvider(Cors.class).request(request).builder(response);
|
||||
}
|
||||
|
||||
public static Cors add(HttpRequest request) {
|
||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
return session.getProvider(Cors.class).request(request);
|
||||
}
|
||||
|
||||
|
|
|
@ -94,6 +94,7 @@ public interface KeycloakSession extends AutoCloseable {
|
|||
* @return
|
||||
* @deprecated Deprecated in favor of {@link #getComponentProvider)
|
||||
*/
|
||||
@Deprecated
|
||||
<T extends Provider> T getProvider(Class<T> clazz, ComponentModel componentModel);
|
||||
|
||||
/**
|
||||
|
@ -203,6 +204,7 @@ public interface KeycloakSession extends AutoCloseable {
|
|||
|
||||
SingleUseObjectProvider singleUseObjects();
|
||||
|
||||
@Override
|
||||
void close();
|
||||
|
||||
/**
|
||||
|
@ -243,4 +245,6 @@ public interface KeycloakSession extends AutoCloseable {
|
|||
*/
|
||||
ClientPolicyManager clientPolicy();
|
||||
|
||||
boolean isClosed();
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* Copyright 2024 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.utils;
|
||||
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
|
||||
public class KeycloakSessionUtil {
|
||||
|
||||
private KeycloakSessionUtil() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link KeycloakSession} currently associated with the thread.
|
||||
*
|
||||
* @return the current session
|
||||
*/
|
||||
public static KeycloakSession getKeycloakSession() {
|
||||
return Resteasy.getContextData(KeycloakSession.class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Associate the {@link KeycloakSession} with the current thread.
|
||||
* <br>Warning: should not be called directly. Keycloak will manage this.
|
||||
*
|
||||
* @param session
|
||||
* @return the existing {@link KeycloakSession} or null
|
||||
*/
|
||||
public static KeycloakSession setKeycloakSession(KeycloakSession session) {
|
||||
return Resteasy.pushContext(KeycloakSession.class, session);
|
||||
}
|
||||
|
||||
}
|
|
@ -120,11 +120,7 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
|||
@Override
|
||||
public ClientConnection getConnection() {
|
||||
if (clientConnection == null) {
|
||||
synchronized (this) {
|
||||
if (clientConnection == null) {
|
||||
clientConnection = createClientConnection();
|
||||
}
|
||||
}
|
||||
clientConnection = createClientConnection();
|
||||
}
|
||||
|
||||
return clientConnection;
|
||||
|
@ -148,11 +144,7 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
|||
@Override
|
||||
public HttpRequest getHttpRequest() {
|
||||
if (request == null) {
|
||||
synchronized (this) {
|
||||
if (request == null) {
|
||||
request = createHttpRequest();
|
||||
}
|
||||
}
|
||||
request = createHttpRequest();
|
||||
}
|
||||
|
||||
return request;
|
||||
|
@ -161,11 +153,7 @@ public abstract class DefaultKeycloakContext implements KeycloakContext {
|
|||
@Override
|
||||
public HttpResponse getHttpResponse() {
|
||||
if (response == null) {
|
||||
synchronized (this) {
|
||||
if (response == null) {
|
||||
response = createHttpResponse();
|
||||
}
|
||||
}
|
||||
response = createHttpResponse();
|
||||
}
|
||||
|
||||
return response;
|
||||
|
|
|
@ -17,9 +17,9 @@
|
|||
|
||||
package org.keycloak.services;
|
||||
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.representations.idm.OAuth2ErrorRepresentation;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
import jakarta.ws.rs.WebApplicationException;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
|
@ -55,10 +55,10 @@ public class ErrorResponseException extends WebApplicationException {
|
|||
|
||||
@Override
|
||||
public Response getResponse() {
|
||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
if (session != null) {
|
||||
// This has to happen, since calling getResponse() with non-null result leads to
|
||||
// directly returning the result instead of
|
||||
// directly returning the result instead of
|
||||
// propagating exception to KeycloakErrorHandler.toResponse(Throwable) which would ensure rollback on other exception types.
|
||||
//
|
||||
// See org.jboss.resteasy.core.ExceptionHandler.unwrapException(HttpRequest, Throwable, RESTEasyTracingLogger)
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package org.keycloak.services.error;
|
||||
|
||||
import static org.keycloak.common.util.Resteasy.getContextData;
|
||||
import static org.keycloak.services.resources.KeycloakApplication.getSessionFactory;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParseException;
|
||||
|
@ -26,6 +25,7 @@ import org.keycloak.theme.beans.MessageBean;
|
|||
import org.keycloak.theme.beans.MessageFormatterMethod;
|
||||
import org.keycloak.forms.login.MessageType;
|
||||
import org.keycloak.theme.freemarker.FreeMarkerProvider;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
import org.keycloak.utils.MediaType;
|
||||
import org.keycloak.utils.MediaTypeMatcher;
|
||||
|
||||
|
@ -55,7 +55,7 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
|
|||
|
||||
@Override
|
||||
public Response toResponse(Throwable throwable) {
|
||||
KeycloakSession session = getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
|
||||
if (session == null) {
|
||||
// errors might be thrown when handling errors from JAX-RS before the session is available
|
||||
|
|
|
@ -16,9 +16,9 @@
|
|||
*/
|
||||
package org.keycloak.services.filters;
|
||||
|
||||
import org.keycloak.common.util.Resteasy;
|
||||
import org.keycloak.headers.SecurityHeadersProvider;
|
||||
import org.keycloak.models.KeycloakSession;
|
||||
import org.keycloak.utils.KeycloakSessionUtil;
|
||||
|
||||
import jakarta.ws.rs.container.ContainerRequestContext;
|
||||
|
||||
|
@ -38,7 +38,7 @@ public class KeycloakSecurityHeadersFilter implements ContainerResponseFilter {
|
|||
|
||||
@Override
|
||||
public void filter(ContainerRequestContext containerRequestContext, ContainerResponseContext containerResponseContext) {
|
||||
KeycloakSession session = Resteasy.getContextData(KeycloakSession.class);
|
||||
KeycloakSession session = KeycloakSessionUtil.getKeycloakSession();
|
||||
|
||||
if (session != null) {
|
||||
SecurityHeadersProvider securityHeadersProvider = session.getProvider(SecurityHeadersProvider.class);
|
||||
|
|
Loading…
Reference in a new issue