13 Replies Latest reply on May 22, 2006 8:11 AM by Thomas Busch

    IllegalStateException in BaseSessionContext.getCallerPrincip

    Thomas Busch Newbie

      Hello,
      I am working on migrating an application from EJB2.1 on Weblogic to EJB3 spec (JBoss) and I have run into a roadblock. In my security SLSB I have a call to ctx.getCallerPrincipal() which throws an IllegalStateException.

      javax.ejb.EJBException: java.lang.IllegalStateException
      at org.jboss.ejb3.tx.Ejb3TxPolicy.handleExceptionInOurTx(Ejb3TxPolicy.java:69)
      ... (tracks back to a proxy of my session bean)
      Caused by: java.lang.IllegalStateException
      at org.jboss.ejb3.BaseSessionContext.getCallerPrincipal(BaseSessionContext.java:168)
      ...

      The security bean extends a generic ServiceBean slsb that I have created. ServiceBean has the following injection for the session context:

      @Resource protected SessionContext sessionContext;

      I am not sure how to track down this bug, I'm pretty new to EJB3 and I'm not really sure how I'm getting an IllegalStateException in a STATELESS session bean but I'm sure that's just a misunderstanding on my part.

      Anywhere that I see an "IllegalStateException" in other posts, it seems to be followed by a description of the exception which at least eludes to the source but this one does not.

      Does anyone have any ideas of where I could look for a solution to my problem?

        • 1. Re: IllegalStateException in BaseSessionContext.getCallerPri
          William DeCoste Apprentice

          Have you specified the caller identify via SecurityAssociation (JBoss proprietary) or LoginContext (JAAS)? If not, the IllegalStateException is because there is no caller identity. I have added a better IllegalStateException message for EJB3 RC6.

          • 2. Re: IllegalStateException in BaseSessionContext.getCallerPri
            William DeCoste Apprentice

            The spec specifies that getCallerPrincipal never returns null - thus the Exception

            • 3. Re: IllegalStateException in BaseSessionContext.getCallerPri
              Thomas Busch Newbie

              I am confused as to how to specify this. I'd prefer to use something non-proprietary so that the app can be dropped on other servers if it needs to be, so JAAS is probably the better solution for me, but I'll take what I can get for now!

              Thank you for explaining the exception, hopefully I can get past this soon!

              • 4. Re: IllegalStateException in BaseSessionContext.getCallerPri
                Bill Burke Master

                unfortunately, J2EE only defines the authentication mechanism for the web tier. It does not define it for EJB invocations so you will have no portable solution.

                • 5. Re: IllegalStateException in BaseSessionContext.getCallerPri
                  Thomas Busch Newbie

                  Ok, well, are there any examples of how to implement this for JBoss?

                  I really appreciate the help!

                  • 6. Re: IllegalStateException in BaseSessionContext.getCallerPri
                    Thomas Busch Newbie

                    Just a bump, does anyone have a link to a tutorial, trail or otherwise informative article explaining how to set up the above mentioned LoginContext?

                    • 7. Re: IllegalStateException in BaseSessionContext.getCallerPri
                      milan wölke Newbie

                      Hi,
                      I can tell u how we set it up in our rich client environment. I dont know if that is directly applicable for u, but I guess its better than nothing.

                      First of all, on the client side, u have to create a LoginContext, and login:

                      CallbackHandler cbhdl = new XXXCallbackHandler();
                      LoginContext lctx = new LoginContext("JBossClientLogin", callbackHandler);
                      try{
                      lctx.login();
                      }catch(LoginException exc){
                      //...
                      }


                      "JBossClientLogin" refers to an entry in a login configuration file. This file has to be specified as a parameter to the jvm, at least in our case:

                      -Djava.security.auth.login.config="${workspace_loc:your_proj}\login.config"


                      Thats the argument string to the jvm, as we use it in eclipse.

                      The file itself looks like this:

                      JBossClientLogin {
                       org.jboss.security.ClientLoginModule required;
                      };


                      The callbackhandler referred to in the first excerpt, is the component, which gathers the login information. This can be achieved by a dialog, or by cfg, or whatever. JAAS supplies you with a bunch of ready-to-use callbackhandlers, which should be sufficent for most scenarios.
                      (see javadoc of jaas, or:
                      http://java.sun.com/j2se/1.5.0/docs/guide/security/jaas/JAASRefGuide.html)

                      When login is called, the callbackhandler collects the needed information, an provides them to the LoginModule defined in the login.config file. The JBoss ClientLoginModule then simply copies the information to the application server, where u are able to access it using getCallerPrincipal(), for example.

                      To enable authentification on the application server, until now no authentication has been done, you have to define a security domain for your enterprise beans. But thats another story.

                      hope I could help,
                      regards, milan wölke

                      • 8. Re: IllegalStateException in BaseSessionContext.getCallerPri
                        Jens Elkner Apprentice

                        Since no standard available, I use a much simpler approach, i.e. without a callback handler or java.security.auth.login.config:

                        @Override
                         public boolean authenticate(String name, char[] password, String[] roleNames)
                         throws Exception
                         {
                         if (roleNames == null) {
                         log.warn("Software bug: must set user roles before trying this");
                         return false;
                         }
                         SecurityAssociation.clear();
                         SecurityAssociation.setPrincipal(new SimplePrincipal(name));
                         SecurityAssociation.setCredential(password);
                         InitialContext ctx = getInitialContext();
                         roleNameSuccess = null;
                         AccountDaoR test = (AccountDaoR) ctx.lookup("accounting/AccountManager/remote");
                         roleNameSuccess = test.hasRoles(roleNames);
                         return roleNameSuccess != null && roleNameSuccess.length > 0;
                         }


                        • 9. Re: IllegalStateException in BaseSessionContext.getCallerPri
                          Luigi Putanesca Newbie

                          There are a couple of potential problems with setting the SecurityAssociation directly. You can configure the login module to set the 'server' flag which determines whether your user is logged into a ThreadLocal or is available to all threads in the server. This is important if you have different users logged into different threads

                          Also, if you have a previously logged in user which you may want to restore later on, the login module provides a restore login identity which behind the scenes manages a thread local stack.

                          • 10. Re: IllegalStateException in BaseSessionContext.getCallerPri
                            Thomas Busch Newbie

                            Thanks, this helps a bit. It doesn't quite solve my problem though -- at this point in the application, the user is already logged in, or they are not going to log in. We use form authentication through j_security_check.

                            What my session bean is trying to do is to get ahold of the user that is authenticated, if any, so that it may check some other information about the user.

                            I know in the past, when we were still on weblogic and EJB2.1, all we had to do was get the SessionContext and then we could call methods such as findPrincipal() and isCallerInRole() but as I'm finding out, this doesn't work in EJB3.

                            This may still be a lack of understanding on my part, but I do appreciate all the help!

                            • 11. Re: IllegalStateException in BaseSessionContext.getCallerPri
                              Thomas Busch Newbie

                              After doing some research, I am beginning to believe that this should work the way I have described. Here is my evidence:

                              In section 4.5.2 of the EJB 3.0 spec (EJBCore) the operations allowed in methods of a stateless session bean are defined, and the IllegalStateException is to be thrown if these rules are broken. It states that a call to EJBContext's .getCallerPrincipal() method is allowed from any business method in the session bean. I am calling getCallerPrincipal from a business method so therefore should not be getting an illegalStateException.

                              Also, in the EJB Container Provider's Responsibilities, section 16.6.5 states that

                              "The EJB container must provide access to the caller?s security context information from the enterprise
                              beans? instances via the getCallerPrincipal()"
                              and also

                              "If the security identity of the caller has not been established, the container
                              returns the container?s representation of the unauthenticated identity."

                              Now, since I am calling the method from a valid location, I should be getting back the user principal, or a default user principal if I have not logged in.

                              Earlier it was mentioned that I needed to set the caller identity via LoginContext. I guess my questions come down to this:

                              #1 Why do I need to do this? I am using j_security_check through form authentication and from what I have read, jboss automatically sets up a LoginContext when this is done, also it is not mentioned in the spec that I need LoginContext in order to call getCallerPrincipal().

                              #2 How do I go about setting up the caller identity in LoginContext, especially if all of the authentication is handled by JBoss through j_security_check. Every document I've found on the subject shows me how to take the user's input of the username and password then basically do a login using callbackhandlers and a whole bunch of other stuff that I have never seen.

                              I guess what I'm getting at is that there has to be an easier way to do this, it's hard to believe that something like this got harder to do between EJB 2.1 and EJB 3.0, which has simplified things in so many other ways.

                              • 12. Re: IllegalStateException in BaseSessionContext.getCallerPri
                                Chris Hornsey Newbie

                                The last post says it all. Everyone has the expectation that the subject from the caller should be easily transpoerted to the ejb3 container. Even the ejb3 spec makes that assertation (see previous post).

                                So why am I doing to logons again?

                                • 13. Re: IllegalStateException in BaseSessionContext.getCallerPri
                                  Thomas Busch Newbie

                                  I'm sorry for not posting this sooner - Here is how I solved this problem without writing my own authentication:

                                  First, make sure to set up login-config.xml in <jboss-home>/server/default/conf. Here is an example

                                  
                                   <application-policy name="other">
                                   <authentication>
                                   <login-module code = "org.jboss.security.auth.spi.LdapExtLoginModule" flag = "required">
                                   <module-option name = "unauthenticatedIdentity">Guest</module-option>
                                   <module-option name="java.naming.provider.url">ldap://<yourldapserver></module-option>
                                   <module-option name="bindDN">CN=My LDAP Account,OU=GroupOU=People,DC=domain,DC=com</module-option>
                                   <module-option name="bindCredential">password</module-option>
                                   <module-option name="baseCtxDN">DC=domain, DC=com</module-option>
                                   <module-option name="searchScope">SUBTREE_SCOPE</module-option>
                                   <module-option name="allowEmptyPasswords">false</module-option>
                                   </login-module>
                                   </authentication>
                                   </application-policy>
                                  


                                  I really should point out that if you don't have that last line, setting allowEmptyPassword=false, by default jboss will authenticate you if you leave the password blank. (Is this really what we want as a default??)

                                  Anyways, once this is set up it will work in some places, but I found that in my session beans that were actually calling ctx.getCallerPrincipal(), I needed this annotation at the top (before the class declaration):

                                  @SecurityDomain("other")
                                  


                                  Where "other" is whatever you named your security domain in login-config.xml (as described above).

                                  So in conclusion to my last comment, Yes, there is an easier way. I'm not sure why this was so difficult to find, at least for me!