6 Replies Latest reply on Feb 23, 2011 11:47 AM by wolfgangknauf

    Secure access to an EJB3.0

    luftballon

      Hi @ all,

      i try to implement a secure access to an EJB and have "javax.ejb.EJBAccessException: Invalid User". Does someone see something wrong or suspicious in the following code? (the security domain "mySecurityDomain" is properly binded in JNDI)

      Formated and colourful version of the code: http://pastebin.com/f74dbf9e6

      SECURITY DOMAIN DEFINITION in login-config.xml------------------------------------------
      <application-policy name="mySecurityDomain">

      <login-module code="org.jboss.security.auth.spi.UsersRolesLoginModule"
      flag="required">
      <module-option name="usersProperties">props/myProps/users.properties</module-option>
      <module-option name="rolesProperties">props/myProps/roles.properties</module-option>
      </login-module>

      </application-policy>

      props/myProps/roles.properties----------------------------------------------------------
      guest=guestRole
      user=userRole,guestRole
      admin=adminRole,userRole,guestRole

      props/myProps/users.properties----------------------------------------------------------
      admin=adminpas
      user=userpas
      guest=guestpas

      BEANINTERFACE----------------------------------------------------------------------------
      package ejb;

      import javax.ejb.Remote;

      @Remote
      public interface Secure {
      public String forAll();
      public String forUsers();
      public String forAdmins();
      public String forNoOne();
      }


      BEAN-------------------------------------------------------------------------------------
      package ejb;

      import javax.annotation.security.DenyAll;
      import javax.annotation.security.PermitAll;
      import javax.annotation.security.RolesAllowed;
      import javax.ejb.Stateless;

      import org.jboss.ejb3.annotation.SecurityDomain;

      //import org.jboss.security.annotation.SecurityDomain;


      @Stateless
      @SecurityDomain("mySecurityDomain")
      @RolesAllowed({"guestRole", "userRole", "adminRole"})
      public class SecureBean implements Secure {

      @RolesAllowed("adminRole")
      public String forAdmins() {
      return "forAdmins";
      }

      @PermitAll
      public String forAll() {
      return "forAll";
      }
      @DenyAll
      public String forNoOne() {
      return "forNoOne";
      }
      @RolesAllowed("userRole")
      public String forUsers() {
      return "forUsers";
      }
      }

      CLIENT-------------------------------------------------------------------------------------
      package client;

      import java.util.Properties;

      import javax.naming.Context;
      import javax.naming.InitialContext;
      import javax.naming.NamingException;

      import ejb.Secure;

      public class SecureClient {
      public static void main(String[] args) {
      Context ctx;
      try {
      Properties props = new Properties();
      props.put(Context.SECURITY_PRINCIPAL, "guest");
      props.put(Context.SECURITY_CREDENTIALS, "guestpas");
      ctx = new InitialContext(props);
      Secure bean = (Secure)ctx.lookup("SecureBean/remote");
      System.out.println(bean.forAll());
      } catch (NamingException e) {
      e.printStackTrace();
      }
      }
      }

      EXCEPTION-------------------------------------------------------------------------------------
      Exception in thread "main" javax.ejb.EJBAccessException: Invalid User
      at org.jboss.ejb3.security.Ejb3AuthenticationInterceptorv2.invoke(Ejb3AuthenticationInterceptorv2.java:165)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.ENCPropagationInterceptor.invoke(ENCPropagationInterceptor.java:41)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.asynchronous.AsynchronousInterceptor.invoke(AsynchronousInterceptor.java:106)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.BlockContainerShutdownInterceptor.invoke(BlockContainerShutdownInterceptor.java:67)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.aspects.currentinvocation.CurrentInvocationInterceptor.invoke(CurrentInvocationInterceptor.java:67)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.stateless.StatelessContainer.dynamicInvoke(StatelessContainer.java:487)
      at org.jboss.ejb3.session.InvokableContextClassProxyHack._dynamicInvoke(InvokableContextClassProxyHack.java:53)
      at org.jboss.aop.Dispatcher.invoke(Dispatcher.java:91)
      at org.jboss.aspects.remoting.AOPRemotingInvocationHandler.invoke(AOPRemotingInvocationHandler.java:82)
      at org.jboss.remoting.ServerInvoker.invoke(ServerInvoker.java:908)
      at org.jboss.remoting.transport.socket.ServerThread.completeInvocation(ServerThread.java:742)
      at org.jboss.remoting.transport.socket.ServerThread.processInvocation(ServerThread.java:695)
      at org.jboss.remoting.transport.socket.ServerThread.dorun(ServerThread.java:522)
      at org.jboss.remoting.transport.socket.ServerThread.run(ServerThread.java:230)
      at org.jboss.remoting.MicroRemoteClientInvoker.invoke(MicroRemoteClientInvoker.java:206)
      at org.jboss.remoting.Client.invoke(Client.java:1708)
      at org.jboss.remoting.Client.invoke(Client.java:612)
      at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:60)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:61)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.security.client.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:65)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.aspects.remoting.PojiProxy.invoke(PojiProxy.java:62)
      at $Proxy3.invoke(Unknown Source)
      at org.jboss.ejb3.proxy.handler.ProxyInvocationHandlerBase.invoke(ProxyInvocationHandlerBase.java:261)
      at org.jboss.ejb3.proxy.handler.session.SessionSpecProxyInvocationHandlerBase.invoke(SessionSpecProxyInvocationHandlerBase.java:101)
      at $Proxy2.forAll(Unknown Source)
      at client.SecureClient.main(SecureClient.java:21)
      at org.jboss.aspects.remoting.InvokeRemoteInterceptor.invoke(InvokeRemoteInterceptor.java:72)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.aspects.tx.ClientTxPropagationInterceptor.invoke(ClientTxPropagationInterceptor.java:61)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.security.client.SecurityClientInterceptor.invoke(SecurityClientInterceptor.java:65)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.ejb3.remoting.IsLocalInterceptor.invoke(IsLocalInterceptor.java:74)
      at org.jboss.aop.joinpoint.MethodInvocation.invokeNext(MethodInvocation.java:102)
      at org.jboss.aspects.remoting.PojiProxy.invoke(PojiProxy.java:62)
      at $Proxy3.invoke(Unknown Source)
      at org.jboss.ejb3.proxy.handler.ProxyInvocationHandlerBase.invoke(ProxyInvocationHandlerBase.java:261)
      at org.jboss.ejb3.proxy.handler.session.SessionSpecProxyInvocationHandlerBase.invoke(SessionSpecProxyInvocationHandlerBase.java:101)
      at $Proxy2.forAll(Unknown Source)
      at client.SecureClient.main(SecureClient.java:21)

        • 1. Re: Secure access to an EJB3.0
          luftballon

          Problem solved: the login must be done over the org.jboss.security.client.SecurityClient

          public class SecureClient {
          public static void main(String[] args) {
          Context ctx;
          try {
          SecurityClient securityClient = SecurityClientFactory.getSecurityClient();
          securityClient.setSimple("admin", "adminpas");
          securityClient.login();
          ctx = new InitialContext();
          Secure bean = (Secure)ctx.lookup("SecureBean/remote");
          System.out.println(bean.forUsers());
          } catch (NamingException e) {
          e.printStackTrace();
          } catch (EJBAccessException e){
          System.out.println("Nicht berechtigt");
          } catch (Exception e) {
          e.printStackTrace();
          }
          }
          }

          • 2. Re: Secure access to an EJB3.0
            alihamza7

            Thanks for posting the solution, although I would've prefered to see a non jboss platform specific solution to this problem.

            • 3. Re: Secure access to an EJB3.0
              pablo.fraga1975

              Hi,

               

              I had the same problem, first i was trying to authenticate the client with the following code:

               

                        Properties env = new Properties();

                        env.setProperty(Context.SECURITY_PRINCIPAL, "caja");

                        env.setProperty(Context.SECURITY_CREDENTIALS, "password");

                        env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory");

                        env.setProperty(Context.PROVIDER_URL, "localhost:1099");

                        InitialContext ctx = new InitialContext(env);

               

              Debugging server side i found that principal doesn't propagate correctly, crawling the web i found this link:

               

              http://msikora.typepad.com/michael_sikora_on_java_ee/2009/03/converting-to-jboss500ga-ejb3-security.html

               

              Where it says that i had to use the following code:

               

                        SecurityClient securityClient = SecurityClientFactory.getSecurityClient();

                        securityClient.setSimple("caja", "password");

                        securityClient.login();

                        InitialContext ctx = new InitialContext();

               

              The problem is that aparently org.jboss.security.client.SecurityClient and org.jboss.security.client.SecurityClientFactory appears to apply to JBoss 5.0.0 and not to JBoss 4.2.3 the one we still use in our production environment.

               

              Any help would be apreciated!

               

              Thanks,

               

              Pablo.

              • 4. Secure access to an EJB3.0
                wolfgangknauf

                Hi Pablo,

                 

                in AS 4.2, you might use something like this:

                 

                public class SecurityClientCallbackHandler implements CallbackHandler

                {

                  public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException

                  {

                    //loop over parameter Callbacks

                    for (int intIndexCallback = 0; intIndexCallback < callbacks.length; intIndexCallback++)

                    {

                      //NameCallback: set Login

                       if (callbacks[intIndexCallback] instanceof NameCallback)

                      {

                        NameCallback nameCallback = (NameCallback) callbacks[intIndexCallback];

                        nameCallback.setName( "ADMIN" );

                      }

                      //PasswordCallback: set password.

                      else if (callbacks[intIndexCallback] instanceof PasswordCallback)

                      {

                        PasswordCallback passwordCallback = (PasswordCallback) callbacks[intIndexCallback];

                        passwordCallback.setPassword ("ADMIN".toCharArray() );

                      }

                      else

                      {

                        throw new UnsupportedCallbackException (callbacks[intIndexCallback], "Unsupported Callback!");

                      }

                    }

                  }

                }

                 

                And in your client, perform this code to login in:

                 

                      Properties props = new Properties();

                      props.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");

                      props.setProperty(Context.URL_PKG_PREFIXES, "org.jboss.naming.client");

                      props.setProperty(Context.PROVIDER_URL, "jnp://localhost:1099");

                      props.setProperty("j2ee.clientName", "SecurityClient");

                     

                      InitialContext initialContext = new InitialContext(props);

                     

                      //Initialize Login:

                      SecurityClientCallbackHandler callbackHandler = new SecurityClientCallbackHandler();

                      LoginContext loginContext = new LoginContext ("somename", callbackHandler);

                      loginContext.login();

                 

                Note the the "j2ee.clientName" must be declared in a file "jboss-client.xml":

                 

                <?xml version="1.0" encoding="UTF-8"?>

                <!DOCTYPE jboss-client PUBLIC "-//JBoss//DTD Application Client 4.2//EN" "http://www.jboss.org/j2ee/dtd/jboss-client_4_2.dtd" >

                <jboss-client>

                      <jndi-name>SecurityClient</jndi-name>

                      ...

                </jboss-client>

                 

                And the "login context" name (here: "somename") must be declared in a file "auth.conf" in META-INF of your client JAR:

                somename {
                   // jBoss LoginModule
                   org.jboss.security.ClientLoginModule  required
                   ;
                };

                 

                 

                And finally, start your client with a parameter pointing to "auth.conf": -Djava.security.auth.login.config=.../META-INF/auth.conf

                 

                Hope this helps

                 

                Wolfgang

                • 5. Re: Secure access to an EJB3.0
                  pablo.fraga1975

                  Wolfgang,

                   

                  Thanks for your reply!

                   

                  I was trying to understand the example, but honestly i lost my self in the jboss-client.xml descriptor, i don't understand which resource ref do i have to map in that file and for what?

                   

                  I thought it would be easier to call an EJB3 from another client EJB3 using security in JBossAS 4.2.3, just like in the example of JBossAS 5:

                   

                  SecurityClient securityClient = SecurityClientFactory.getSecurityClient();

                  securityClient.setSimple("caja", "password");

                  securityClient.login();

                  InitialContext ctx = new InitialContext();

                   

                  Maybe i mess up myself, but i will try to explain my problem with an example:

                   

                  First i have an EJB3, annotated with security annotations:

                   

                  @Stateless(name = "ProxyIMMEJB")

                  @SecurityDomain("other")

                  @RolesAllowed("architect")

                  @Local(value = ProxyIMMLocal.class)

                  public class ProxyIMMEJBImpl implements ProxyIMMLocal {

                   

                      @Resource SessionContext ctx;

                   

                      @RolesAllowed("architect")

                      public RespuestaIMMTO comprarTicket(TicketTO ticketTO) throws ... {

                          Principal cp = ctx.getCallerPrincipal();

                          log.debug("Principal's name: " + cp.getName());

                          ...

                      }

                  }

                   

                  As you can see "other" indicates that i use JBoss's default authentication mechanism defined in login-config.xml situated in JBOSS_HOME\server\default\conf directory. In my case of "other", login-config.xml uses 2 properties files: users.properties and roles.properties with the following contents:

                   

                  login-config.xml

                   

                     <application-policy name = "other">

                        <authentication>

                            <login-module code = "org.jboss.security.auth.spi.UsersRolesLoginModule"

                               flag = "required">

                                <module-option name="usersProperties">props/users.properties</module-option>

                                <module-option name="rolesProperties">props/roles.properties</module-option>

                                <module-option name="unauthenticatedIdentity">anonymous</module-option>

                            </login-module>

                         </authentication>

                     </application-policy>

                   

                   

                  users.properties

                   

                  caja=password

                   

                   

                  roles.properties

                   

                  caja=architect

                   

                   

                  In another EJB3 client, i tried to call the ProxyIMMEJB bean using standard security code:

                   

                  Properties env = new Properties();

                  env.setProperty(Context.SECURITY_PRINCIPAL, "caja");

                  env.setProperty(Context.SECURITY_CREDENTIALS, "password");

                  env.setProperty(Context.INITIAL_CONTEXT_FACTORY, "org.jboss.security.jndi.JndiLoginInitialContextFactory");

                  env.setProperty(Context.PROVIDER_URL, "localhost:1099");

                  try {

                     InitialContext ctx = new InitialContext(env);

                     proxyIMM = (ProxyIMMLocal) ctx.lookup("estar/ejb/ProxyIMM/local");

                  } catch (NamingException e) {

                     // TODO Auto-generated catch block

                     e.printStackTrace();

                  }

                   

                  Soon i realized, security context was not propagated because i got "javax.ejb.EJBAccessException: Authorization failure" and confirmed later changing @RolesAllowed("architect") with @PermitAll and debugging principal's name.

                   

                          Principal cp = ctx.getCallerPrincipal();

                          log.debug("Principal's name: " + cp.getName());

                   

                  This gave me anonymous.

                   

                  In my scenario, the complete example that you wrote applies? or there is an easiest way?

                   

                  Thank you very much for your patience!

                   

                  Pablo.

                  • 6. Secure access to an EJB3.0
                    wolfgangknauf

                    Hi,

                     

                    I think that my sample applies to your use case, too.

                     

                    One big "!!!" in your code snippets: don't use a Context.INITIAL_CONTEXT_FACTORY of "org.jboss.security.jndi.JndiLoginInitialContextFactory", because this was problematic with JBoss 4.2, and it will no longer work in AS 5 and above. You will have to perform a JAAS login as I described.

                     

                    The security client of AS5 is just a simplified JAAS login, which performs internally similar things as my code ;-).

                     

                    Best regards

                     

                    Wolfgang