1 Reply Latest reply on Sep 4, 2014 2:45 PM by flossware

    Is it possible to take objects created from a custom LoginModule and retrieve those objects from an EJB or servlet

    flossware

      All,

       

      I have a custom LoginModule that looks up some information that'd be great to store and have access to via an EJB (or servlet)...

       

      I do have my own implementation of a java.security.Principal that I could "stuff" this data in...but doing so in my EJB means I'll be coupling to this Principal implementation.

       

      I did see this link:  https://access.redhat.com/documentation/en-US/JBoss_Enterprise_Application_Platform/6.1/html/Development_Guide/Pass_Additional_Security_For_EJB_Authentication.html

       

      I'm not sure if there is a good way to do this?  I'd rather not have to cast to the Principal type in my EJB - unless I must.

       

      Very much appreciate the help ahead of time!

       

      Flossy

        • 1. Re: Is it possible to take objects created from a custom LoginModule and retrieve those objects from an EJB or servlet
          flossware

          I finally found and figured out a solution...

           

          So to be fair, I happened upon this article which gave me the idea:  http://publib.boulder.ibm.com/infocenter/wsdoc400/v6r0/index.jsp?topic=/com.ibm.websphere.iseries.doc/info/ae/ae/tsec_propcustjavaser.html

           

          Basically what I did was the following:

          • I created a serializable (well its Externalizable) object to hold my data being retrieved in the custom LoginModule.  For this example we'll call the object LoginContext.
          • In the custom LoginModule, at the end of the login() method, I populate the data retrieved into the LoginContext and store it as a private credential using Subject.getPrivateCredentials().add(LoginContext)
          • My EJB module has a dependency on the aforementioned classes and use an @SecurityDomain. When retrieving, I simply do PolicyContext.getContext("javax.security.auth.Subject.container").getPrivateCredentials() and then find the object in the returned set whose class is LoginContext.class.

           

          Some "code" may better explain.  Let's assume I want to share a Foo and a Bar that I look up in my custom LoginModule:

           

          public class Foo implements Serializable {

            // Some methods...

          }

           

          public class Bar implements Serializable {

            // Some methods

          }

           

          // Just using serializable here for brief explanation.

          public class LoginContext implements Serializable {

            private Foo foo;

            private Bar bar;

           

            public void setFoo(final Foo foo) {this.foo = foo;}

            public Foo getFoo() {return foo);

           

            public void setBoo(final Foo foo) {this.bar = bar;}

            public Bar getBoo() {return bar;}

          }

           

          // My custom login module:

          public class MyCustomLoginModule implements LoginModule {

              private Subject subject;

              ...

           

              @Override

              public void initial(final Sibject subject, final CallbackHandler  callbackHandler, final Map sharedState, final Map options)  {

                  ...

                  this.subject = subject;

                  ...

              }

           

              @Override

              public boolean login() throws LoginException {

                  ...

                  final Foo foo = ...;

                  final Bar bar = ...;

           

                  final LoginContext = new LoginContext();

                  loginContext.setFoo(foo);

                  loginContext.setBar(bar);

            

                  subject.getPrivateCredentials().add(loginContext);

           

                  ...

              }

           

              ...

          }

           

          // My EJB

          @Local

          public interface LoginContextLocal {

              LoginContext getLoginContext();

          }

           

          @Stateless

          @SecurityDomain("MySecurityDomain")

          public class LoginContextBean implements LoginContextLocal {

              @PermitAll

              @Override

              public LoginContext getLoginContext() {

                  try {

                      final Set set = ((Subject) PolicyContext.getContext("javax.security.auth.Subject.container")).getPrivateCredentials();

                      for (final Object privateCreds : set) {

                          if (LoginContext.class == privateCreds.getClass()) {

                              return (LoginContext) privateCreds;

                         }

                   }

                  catch (final PolicyContextException policyContextException) {

                       ...

                   }

                   return null;

              }

          }

           

          In maven, I did find I had to include the following dependency in both my project for the custom login module and the ejb as follows:

           

          <dependency>

              <groupId>javax.security.jacc</groupId>

              <artifactId>javax.security.jacc-api</artifactId>

              <version>1.5</version>

          </dependency>

           

          I did have some problem retrieving the Subject in the EJB layer if I wasn't using an EJB denoted w/ my security domain - I'd get the following exception:  java.lang.IllegalArgumentException: unknown handler key

           

          As a side note, my custom Login Module is an actual JBoss module (aka lives in the $JBOSS_HOME/modules directory).  I had to do the following in my EJB layer:

          • setup my jboss-deployment-structure.xml to refer to that module.
          • denote the dependency as a provided scope in my Maven pom.xml.  Not doing so results in ClassCastException's when using the aforementioend LoginContext.  I believe separate class loaders are being used which is what causes the ClassCastException.

           

          Above, by using an EJB you can now inject it and use it all over the place