Context: Keycloak 1.9.1.Final and newer versions
Hi,
I have created a custom user federation provider which is a simple variation of the Keycloak's classpath property federation provider example. Instead of reading the usernames in a property file, I fetch then from an external web service.
My trouble is that sometimes I get the following exception when trying to authenticate with a test user:
Failed authentication: java.lang.IllegalStateException: Cannot access delegate without a transaction
at org.keycloak.models.cache.infinispan.UserCacheSession.getDelegate(UserCacheSession.java:78)
at org.keycloak.models.cache.infinispan.UserCacheSession.addUser(UserCacheSession.java:442)
at com.example.keycloak.MyFederationProvider.getUserModel(MyFederationProvider.java:324)
at com.example.keycloak.MyFederationProvider.getUserByUsername(MyFederationProvider.java:206)
at org.keycloak.models.UserFederationManager.getUserByUsername(UserFederationManager.java:237)
at org.keycloak.models.utils.KeycloakModelUtils.findUserByNameOrEmail(KeycloakModelUtils.java:273)
at org.keycloak.authentication.authenticators.browser.AbstractUsernameFormAuthenticator.validateUserAndPassword(AbstractUsernameFormAuthenticator.java:127)
at org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.validateForm(UsernamePasswordForm.java:56)
at org.keycloak.authentication.authenticators.browser.UsernamePasswordForm.action(UsernamePasswordForm.java:49)
at org.keycloak.authentication.DefaultAuthenticationFlow.processAction(DefaultAuthenticationFlow.java:84)
at org.keycloak.authentication.AuthenticationProcessor.authenticationAction(AuthenticationProcessor.java:759)
at org.keycloak.services.resources.LoginActionsService.processFlow(LoginActionsService.java:359)
at org.keycloak.services.resources.LoginActionsService.processAuthentication(LoginActionsService.java:341)
at org.keycloak.services.resources.LoginActionsService.authenticateForm(LoginActionsService.java:386)
...
I can't figure out why this exception occures. I looked at the org.keycloak.models.cache.infinispan.UserCacheSession class and I could see that the exception is thrown when transactionActive variable is false, but I don't understand under what conditions it is set to false.
I tried forcing a transaction with KeycloakModelUtils.runJobInTransaction() method or by adding begin() and commit() arround the addUser() call, but it didn't solved the issue (I got a new error which informs that transaction is already active).
Did you already experienced this exception and know how to avoid it ?
Thanks a lot
I think I could find my error (or at least a workarround).
The getInstance() method of my user federation provider was always returning the same object (a singleton). I updated it in order to make it create a new provider every time the method is called.
This seems to solve the issue.
Related
I'm trying to setup a keycloak instance which connects to a separate, second database for it's user management. I see a lot of jdbc examples online, but none with JPA.
I tried to setup my keycloak to use a separate JpaConnectionProvider to be able to use JPA in my CustomUserStorageProvider. For this, I created a subclass MySecondJpaConnectionProviderFactory of DefaultJpaConnectionProviderFactory which should create my own JpaConnectionProvider for usage in the CustomUserStorageProvider. But when obtaining the JpaConnectionProvider, strange things happen in the factory, even though the connection to the second database can be successfully obtained.
CustomUserStorageProvider using a JpaConnectionProvider created by MySecondJpaConnectionProviderFactory extends DefaultJpaConnectionProviderFactory
First, when calling migration(...) the factory is not able to locate the LiquibaseJpaUpdaterProviderFactory (on line 342), returning null for the lookup, causing a NullPointer on line 344.
void migration(MigrationStrategy strategy, boolean initializeEmpty, String schema, File databaseUpdateFile, Connection connection, KeycloakSession session) {
JpaUpdaterProvider updater = session.getProvider(JpaUpdaterProvider.class, LiquibaseJpaUpdaterProviderFactory.PROVIDER_ID);
JpaUpdaterProvider.Status status = updater.validate(connection, schema);
if (status == JpaUpdaterProvider.Status.VALID) {
logger.debug("Database is up-to-date");
(....)
Since I don't need the migration, I copied the class methods into my own class instead of extending it and removed the migration part, but then keycloak is not able to find necessary classes when creating the entity manager factory on line 286.
Caused by: java.lang.ClassNotFoundException: org.jboss.jandex.DotName
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:581)
Code in DefaultJpaConnectionProviderFactory:
(....)
properties.put(AvailableSettings.CLASSLOADERS, classLoaders);
emf = JpaUtils.createEntityManagerFactory(session, unitName, properties, jtaEnabled);
addSpecificNamedQueries(session, connection);
logger.trace("EntityManagerFactory created");
(....)
All in all, looking at these suspicious errors I'm wondering if I run into a dead end here: Is usage of a second JPA connection supposed to be possible? Am I missing something?
Or should I rather stick to doing it all with jdbc?
The setup:
I'm running keycloak 19 in a docker container where I pre-build according to this official guide. All my SPI's are recognized by keycloak, the jdbc connection to the second db is working.
I'm working on a REST API of a TomEE 7 based web app, which uses Shiro 1.3.2 for security. When an API request comes in, a SecurityManager and a Subject are created, and the latter is bound to a SubjectThreadState. I can call SecurityUtils.getSubject() anywhere in the endpoint code and the subject is always available.
However, problems arise when I try to do the same inside my custom JSON serialiser. It only serialises specific fields in some classes, so I register it on a per-field basis using this annotation:
#JsonSerialize(using = MySerialiser.class)
Long myRelatedItemId;
I wrote my serialiser based on the example code on this page under "2.7. #JsonSerialize". The serialiser needs to perform a cache lookup, and for that it has to have a Shiro subject. There is none because, thanks to the annotation above, I don't call the serialiser manually; instead Jersey calls it. This exception gets thrown (clarification: when I try to run SecurityUtils.getSubject() from the serialiser code):
org.apache.shiro.UnavailableSecurityManagerException: No SecurityManager accessible to the calling code, either bound to the org.apache.shiro.util.ThreadContext or as a vm static singleton. This is an invalid application configuration.
at org.apache.shiro.SecurityUtils.getSecurityManager(SecurityUtils.java:123)
at org.apache.shiro.subject.Subject$Builder.<init>(Subject.java:627)
at org.apache.shiro.SecurityUtils.getSubject(SecurityUtils.java:56)
I have confirmed that everything works if I call something like ObjectMapper().writeValueAsString() manually from the API endpoint code. However, that is definitely not the proper way to do it, because then the endpoint would effectively send and receive strings instead of the objects they are meant to handle.
I don't understand much about the inner workings of Shiro or Jackson, but it seems like the serialisation is being performed inside another thread, where Shiro's SubjectThreadState doesn't exist. Although if threading really is the cause, then I cannot see why Thread.currentThread().getName() returns the same value both inside and outside the serialiser, as does Thread.currentThread().getId().
I have tried a vast number of things to no avail, including:
Upgrading to Shiro 1.4.0.
Upgrading Jackson from 2.7.5 to 2.9.7.
Saving the SecurityManager instance that is created at the start of the API call inside a static ThreadLocal variable of the serialiser class.
Writing my own implementation of MessageBodyWriter which, not surprisingly, is called in exactly the same fashion.
Setting the staticSecurityManagerEnabled parameter to true in the ShiroFilter configuration in my web.xml.
Can anyone suggest how I could make the SecurityManager (or Subject) visible to the serialiser, when it's running in a thread not started by my code (clarification: or, otherwise running in parallel and started by Jersey, as far as I can tell)? Thanks in advance.
Update:
This stack trace was taken inside the serialiser:
<mypackage>.MySerializer.serialize()
com.fasterxml.jackson.databind.ser.BeanPropertyWriter.serializeAsField()
com.fasterxml.jackson.databind.ser.std.BeanSerializerBase.serializeFields()
com.fasterxml.jackson.databind.ser.BeanSerializer.serialize()
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue()
com.fasterxml.jackson.databind.ObjectWriter$Prefetch.serialize()
com.fasterxml.jackson.databind.ObjectWriter.writeValue()
com.fasterxml.jackson.jaxrs.base.ProviderBase.writeTo()
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.invokeWriteTo()
org.glassfish.jersey.message.internal.WriterInterceptorExecutor$TerminalWriterInterceptor.aroundWriteTo()
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed()
org.glassfish.jersey.server.internal.JsonWithPaddingInterceptor.aroundWriteTo()
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed()
org.glassfish.jersey.server.internal.MappableExceptionWrapperInterceptor.aroundWriteTo()
org.glassfish.jersey.message.internal.WriterInterceptorExecutor.proceed()
org.glassfish.jersey.message.internal.MessageBodyFactory.writeTo()
org.glassfish.jersey.server.ServerRuntime$Responder.writeResponse()
org.glassfish.jersey.server.ServerRuntime$Responder.processResponse()
org.glassfish.jersey.server.ServerRuntime$Responder.process()
org.glassfish.jersey.server.ServerRuntime$2.run()
This one was taken in our interceptor class where the Subject is created and bound:
<mypackage>.MySecurityInterceptor.createSession()
sun.reflect.NativeMethodAccessorImpl.invoke0()
sun.reflect.NativeMethodAccessorImpl.invoke()
sun.reflect.DelegatingMethodAccessorImpl.invoke()
java.lang.reflect.Method.invoke()
org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke()
org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed()
org.apache.openejb.monitoring.StatsInterceptor.record()
org.apache.openejb.monitoring.StatsInterceptor.invoke()
sun.reflect.GeneratedMethodAccessor111.invoke()
sun.reflect.DelegatingMethodAccessorImpl.invoke()
java.lang.reflect.Method.invoke()
org.apache.openejb.core.interceptor.ReflectionInvocationContext$Invocation.invoke()
org.apache.openejb.core.interceptor.ReflectionInvocationContext.proceed()
org.apache.openejb.core.interceptor.InterceptorStack.invoke()
org.apache.openejb.core.stateless.StatelessContainer._invoke()
org.apache.openejb.core.stateless.StatelessContainer.invoke()
org.apache.openejb.core.ivm.EjbObjectProxyHandler.synchronizedBusinessMethod()
org.apache.openejb.core.ivm.EjbObjectProxyHandler.businessMethod()
org.apache.openejb.core.ivm.EjbObjectProxyHandler._invoke()
org.apache.openejb.core.ivm.BaseEjbProxyHandler.invoke()
com.sun.proxy.$Proxy279.getEntity()
org.openapitools.api.impl.MyApiServiceImpl.getEntity()
org.openapitools.api.MyApi.getEntity()
sun.reflect.NativeMethodAccessorImpl.invoke0()
sun.reflect.NativeMethodAccessorImpl.invoke()
sun.reflect.DelegatingMethodAccessorImpl.invoke()
java.lang.reflect.Method.invoke()
org.glassfish.jersey.server.model.internal.ResourceMethodInvocationHandlerFactory$1.invoke()
org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher$1.run()
org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.invoke()
org.glassfish.jersey.server.model.internal.JavaResourceMethodDispatcherProvider$ResponseOutInvoker.doDispatch()
org.glassfish.jersey.server.model.internal.AbstractJavaResourceMethodDispatcher.dispatch()
org.glassfish.jersey.server.model.ResourceMethodInvoker.invoke()
org.glassfish.jersey.server.model.ResourceMethodInvoker.apply()
org.glassfish.jersey.server.model.ResourceMethodInvoker.apply()
org.glassfish.jersey.server.ServerRuntime$2.run()
There are 46 more calls that are identical in both traces after that last line, so I excluded them. They contain a bunch of org.apache.catalina.core and org.glassfish.jersey.
Take a look at Shiro's Subject Thread Association doc
I need to record user login and logout events using the new session id generated after a successful authentication when protecting against session fixation. I noticed the sessionId value in the Details object of the Authentication object does not get updated after the new session is created. I've spent the last two days reading a lot of the javadoc and searching for answers on the web. I found this old post by Luke Taylor. I tried his suggestion but it did not seem to work, so I tried creating a CustomWebAuthenticationDetails that allowed me to set the sessionId to the new value (I couldn't extend WebAuthenticationDetails because instance variable sessionId is final) and then using a CustomSessionAuthenticationStrategy that extended SessionFixationProtectionStrategy and overrode method onSessionChange. In this method, I updated the Details object in the Authentication object with an instance of CustomWebAuthenticationDetails that contained the new sessionId. This seemed to work, but once I logged out and logged back in, I got a java.lang.ClassCastException: org.springframework.security.web.authentication.WebAuthenticationDetails cannot be cast to org...CustomWebAuthenticationDetails. I understood why I got the exception, so after searching some more, I tried creating a CustomWebAuthenticationDetailsSource and injecting it into an instance of UsernamePasswordAuthenticationFilter, but got an error saying "authenticationManager must be specified". When I tried to also inject the AuthenticationManager, I realized I couldn't because I'm using a RemoteAuthenticationManager with my application. At this point, I don't know what else to to try, so any suggestions will be much appreciated.
I would also like to know the reason why the sessionId is not updated in the Authentication object after a new session is created to protect against session fixation. Would doing this make the application less secure? If so, how?
Thanks,
Pat
I have AKKA actors running in Play 2 application. There are a list of POJO objects retrieved from database and pass along in a message to actors. When an actor starts processing these objects, it will throw this exception. I guess it tries to read data from DB because of lazy loading of ebean. This happens when running in test cases. I haven't tested in normal application env.
Attempting to obtain a connection from a pool that has already been shutdown
at com.avaje.ebeaninternal.server.transaction.TransactionManager.createQueryTransaction(TransactionManager.java:356)
at com.avaje.ebeaninternal.server.core.DefaultServer.createQueryTransaction(DefaultServer.java:2021)
at com.avaje.ebeaninternal.server.core.OrmQueryRequest.initTransIfRequired(OrmQueryRequest.java:241)
at com.avaje.ebeaninternal.server.core.DefaultServer.findList(DefaultServer.java:1468)
at com.avaje.ebeaninternal.server.core.DefaultBeanLoader.loadBean(DefaultBeanLoader.java:360)
at com.avaje.ebeaninternal.server.core.DefaultServer.loadBean(DefaultServer.java:526)
at com.avaje.ebeaninternal.server.loadcontext.DLoadBeanContext.loadBean(DLoadBeanContext.java:143)
at com.avaje.ebean.bean.EntityBeanIntercept.loadBean(EntityBeanIntercept.java:548)
at com.avaje.ebean.bean.EntityBeanIntercept.preGetter(EntityBeanIntercept.java:638)
at models.MemberInfo._ebean_get_type(MemberInfo.java:4)
at models.MemberInfo.getType(MemberInfo.java:232)
at actors.MessageWorker.doSendToIOS(MessageWorker.java:161)
at actors.MessageWorker.onReceive(MessageWorker.java:97)
at akka.actor.UntypedActor$$anonfun$receive$1.apply(UntypedActor.scala:154)
at akka.actor.UntypedActor$$anonfun$receive$1.apply(UntypedActor.scala:153)
at akka.actor.Actor$class.apply(Actor.scala:311)
at akka.actor.UntypedActor.apply(UntypedActor.scala:93)
at akka.actor.ActorCell.invoke(ActorCell.scala:619)
at akka.dispatch.Mailbox.processMailbox(Mailbox.scala:196)
at akka.dispatch.Mailbox.run(Mailbox.scala:178)
at akka.dispatch.ForkJoinExecutorConfigurator$MailboxExecutionTask.exec(AbstractDispatcher.scala:505)
at akka.jsr166y.ForkJoinTask.doExec(ForkJoinTask.java:259)
at akka.jsr166y.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:974)
at akka.jsr166y.ForkJoinPool.runWorker(ForkJoinPool.java:1478)
at akka.jsr166y.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:104)
Although I'm not sure if it's relevant for you, I'll tell my story. I had the same error message coming up when running my test-cases, without using actors.
First note that, during stopping a Play application, its data-sources are closed.
Since many of my test-cases require a running Application in scope, I was using the WithApplication helper around each test-case. The problem in my case was that my DB-access object was a singleton (a Scala object) initializing its Datasource only once. Since that object was never re-instantiated between test-cases, the closed datasource remained there, resulting in the mentioned error.
The solution in my case was to make sure the datasource was re-created between test-cases.
I am trying to figure out how to create a stateless spring bean that wraps a group of calls to salesforce web service API. My understanding is that the process for calling salesforce is something like this:
Call the login service to Log into salesforce
Make a series of service calls
Call the logout
To have a stateless wrapper it seems right pattern is to have each method in the bean perform all three steps above. e.g. (pseudocode)
binding.login();
binding.upsert(….);
binding.upsert(….);
binding.logout();
Is this a good practice? It seems it would be a costly way just to keep the bean stateless. Would it be preferable in this instance to make the bean request scope?
I agree with Anup, you should take a look at the sfdc wsc.
If you want go entirely stateless you will need to know the following things about a salesforce connection.
every action must have an endpoint url, something like na6.salesforce.com or na7.salesforce.com
every action must have a valid SID(session id) for the action to complete.
When you login to salesforce the original endpoint is login.salesforce.com and if you are sucessfull you will be returned a valid endpoint(na6.salesforce.com) and a SID.
You can then drop that endpoint/sid into a singleton and share it with as many spring beans as you want. In each bean you will need to create a new SoapBindingStub and set the endpoint and sid before you execute an insert/upsert/update/etc. action.
That sid will expire after a period of inactivity (anywhere from 30-120 minutes depending upon setting) so you should catch any exceptions and relogin using the singleton object.
Dont worry about two beans casuing the singleton relogin at the same time because salesforce will return the sames sid to both.
Have you tried SFDC WSC library? It's easy to use, and fits the use case that you are trying to accomplish.