p2-inc / keycloak-events

Useful Keycloak event listener implementations and utilities.
https://phasetwo.io
Other
168 stars 35 forks source link

Error when creating a user in UserEventListenerProviderFactory #34

Closed stilet closed 1 year ago

stilet commented 1 year ago

Keycloak 21.0.1, 22.0.1. Release module 0.17, 0.18, 0.19-SNAPSHOT

ERROR [org.keycloak.services.error.KeycloakErrorHandler] (executor-thread-4) Uncaught server error: java.lang.IllegalStateException: Cannot access delegate without a transaction
        at org.keycloak.models.cache.infinispan.UserCacheSession.getDelegate(UserCacheSession.java:114)
        at org.keycloak.models.cache.infinispan.UserCacheSession.getUserById(UserCacheSession.java:200)
        at io.phasetwo.keycloak.events.UserEventListenerProviderFactory$1$1.commitImpl(UserEventListenerProviderFactory.java:57)
        at org.keycloak.models.AbstractKeycloakTransaction.commit(AbstractKeycloakTransaction.java:48)
        at org.keycloak.services.DefaultKeycloakTransactionManager.commit(DefaultKeycloakTransactionManager.java:146)
        at org.keycloak.services.resources.admin.UsersResource.createUser(UsersResource.java:173)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
        at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.base/java.lang.reflect.Method.invoke(Method.java:568)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:154)
        at org.jboss.resteasy.core.MethodInjectorImpl.invoke(MethodInjectorImpl.java:118)
        at org.jboss.resteasy.core.ResourceMethodInvoker.internalInvokeOnTarget(ResourceMethodInvoker.java:560)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTargetAfterFilter(ResourceMethodInvoker.java:452)
        at org.jboss.resteasy.core.ResourceMethodInvoker.lambda$invokeOnTarget$2(ResourceMethodInvoker.java:413)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invokeOnTarget(ResourceMethodInvoker.java:415)
        at org.jboss.resteasy.core.ResourceMethodInvoker.invoke(ResourceMethodInvoker.java:378)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:174)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:142)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:168)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:142)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invokeOnTargetObject(ResourceLocatorInvoker.java:168)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:131)
        at org.jboss.resteasy.core.ResourceLocatorInvoker.invoke(ResourceLocatorInvoker.java:33)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:429)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$invoke$4(SynchronousDispatcher.java:240)
        at org.jboss.resteasy.core.SynchronousDispatcher.lambda$preprocess$0(SynchronousDispatcher.java:154)
        at org.jboss.resteasy.core.interception.jaxrs.PreMatchContainerRequestContext.filter(PreMatchContainerRequestContext.java:321)
        at org.jboss.resteasy.core.SynchronousDispatcher.preprocess(SynchronousDispatcher.java:157)
        at org.jboss.resteasy.core.SynchronousDispatcher.invoke(SynchronousDispatcher.java:229)
        at io.quarkus.resteasy.runtime.standalone.RequestDispatcher.service(RequestDispatcher.java:82)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.dispatch(VertxRequestHandler.java:147)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:84)
        at io.quarkus.resteasy.runtime.standalone.VertxRequestHandler.handle(VertxRequestHandler.java:44)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:200)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:58)
        at io.quarkus.vertx.http.runtime.options.HttpServerCommonHandlers$1.handle(HttpServerCommonHandlers.java:36)
        at io.vertx.ext.web.impl.RouteState.handleContext(RouteState.java:1284)
        at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:177)
        at io.vertx.ext.web.impl.RoutingContextWrapper.next(RoutingContextWrapper.java:200)
        at org.keycloak.quarkus.runtime.integration.web.QuarkusRequestFilter.lambda$createBlockingHandler$0(QuarkusRequestFilter.java:82)
        at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:576)
        at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2513)
        at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1538)
        at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
        at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
        at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
        at java.base/java.lang.Thread.run(Thread.java:833)

my class:

package io.phasetwo.keycloak.events;

import com.google.auto.service.AutoService;
import lombok.extern.jbosslog.JBossLog;
import org.keycloak.Config;
import org.keycloak.events.EventListenerProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;

@JBossLog
@AutoService(EventListenerProviderFactory.class)
public class MyUserAddRemove extends UserEventListenerProviderFactory {

    @Override
    public String getId() {
      return "ext-event-myuseraddremove";
    }

    @Override
    UserChangedHandler getUserChangedHandler() {
      return new UserChangedHandler() {
        @Override
        void onUserAdded(KeycloakSession session, RealmModel realm, UserModel user) {
          log.infof("User %s added to Realm %s", user.getUsername(), realm.getName());
        }

        @Override
        void onUserRemoved(KeycloakSession session, RealmModel realm, UserModel user) {
          log.infof("User %s removed from Realm %s", user.getUsername(), realm.getName());
        }
      };
    }

  }
xgp commented 1 year ago

@stilet thanks for the report. How are you triggering the event?

xgp commented 1 year ago

@stilet can you try building https://github.com/p2-inc/keycloak-events/tree/xgp/issue-34 and running your test again?

The exception looks like the commitImpl isn't getting run in the transaction associated with the request scope. This may have changed in some Keycloak update.

This branch does it from a fresh transaction/session. The upside is that this will probably solve the problem. The downside is the KeycloakSession you have access to in the onUserAdded/onUserRemoved is not the session associated with the request scope.

stilet commented 1 year ago

@stilet thanks for the report. How are you triggering the event?

I create a user from AdminUI: image

stilet commented 1 year ago

@stilet can you try building https://github.com/p2-inc/keycloak-events/tree/xgp/issue-34 and running your test again?

The exception looks like the commitImpl isn't getting run in the transaction associated with the request scope. This may have changed in some Keycloak update.

This branch does it from a fresh transaction/session. The upside is that this will probably solve the problem. The downside is the KeycloakSession you have access to in the onUserAdded/onUserRemoved is not the session associated with the request scope.

Great! Everything works...