1 Reply Latest reply on Sep 12, 2012 11:10 AM by sfcoy

    simple security sample for JBoss AS 7 (with LDAP and CDI) -> wtf??? did I mix something up?

    robbatt

      Intro:

      I used JBoss 4 during my final thesis at university, but used only a little part of it due to the complex configuration that was really hard to figure out on a short timeline. Now I am coming back to JBoss 7 cause I need it for work. I really like what you did in terms of reducing configuration complexity. All configuration in one file is really helping when you are just getting started. Basically I need a setup that works out of the box with the delivered JBoss AS7 modules, so that I can tell my client to install jboss binaries, throw that .war in the deploy folder, overwrite the standalone.xml in the config folder and hit my launcher skript to start everything. So far so good, seems it does all I need.

       

      The only thing I got stuck with is the configuration of the application security. I successfully got the security running for the http managment console via ldap, really nice, a hand full lines of code, real easy. But the security config for applications ... wtf ... I've been searching for 2 days now and can't get anything together that seems to do the configuration as intended by the creator, so please help me with this ... I am REALLY confused ...

       

      CDI + @SecurityDomain instead of DI through EJBs ...?:

      I really like the implementation of cdi out of the box, that makes everything very slim and saves a lot of code and xml configuration. Makes the use of C&DI available without ejbs ... as people say ... but wait ... this is where it starts to get confusing ...

       

      What I could not find out was how to apply security restrictions to this. As I understood it, there is a security domain in the standalone.xml defined in the security subsystem and some set of annotations for my @WebServlet servlets and @Entity entity beans with that I can secure access to my services or data.

       

      • org.jboss.security.annotation.SecurityDomain (said to be deprecated -> why? would seem valid to me if  referring to a domain defined in standalone.xml)
      • org.jboss.ejb3.annotation.SecurityDomain (said to replace the previous annotation -> why? so do I still need ejbs? is there no way to apply security to a pure cdi application? I thought AS 7 has some sort of built in authentication ... please point me in the right direction if I am mixing something up here ...)

       

      the following annotations will all be ignored if none of the above is declared -> why? shouldn't the default behaviour be that the access is denied (obviously there must have been a reason why some programmer placed that annotation) and there is is an error thrown that tells me why the acces has failed, e.g. no access to ldap to check credentials, etc.

      • javax.annotation.security.DenyAll
      • javax.annotation.security.RolesAllowed
      • javax.annotation.security.DeclareRoles

       

      what the gurus say:

      ... I've read several forum posts about this and there are lots of differnent opinions on how to do it best. Some say just use spring security -> well that means I need to add a whole spring context and have that problem with conversion between cdi and spring objects. Others say go with the old 4 file approach (web.xml, login-config.xml, jboss-web.xml. In this forum there was an approach for a bare minimum setup which stil uses a lot of .xml configuration in web.xml, so I am asking myself if there isn't a better approach ...

       

       

      the outcome of all this is following setup:

      this which works for now, but configured only on xml base (=> url granularity), not annotation based (=> method granularity).

       

       

      %JBOSS_HOME%/standalone/configuration/standalone.xml

      {code:xml}

      <management>

          <security-realms>

              <security-realm name="myCompany_ldap_realm">

                  <authentication>

                      <ldap connection="myCompany_ldap_connection" base-dn="CN=Users,DC=myOffice,DC=myCompany,DC=de">

                          <advanced-filter filter="(sAMAccountName={0})"/>

                      </ldap>

                  </authentication>

              </security-realm>

          </security-realms>

          <outbound-connections>

              <ldap     name="myCompany_ldap_connection"

                      url="ldap://active-dir1.myOffice.myCompany.de:389/"

                      search-dn="CN=myLdapAdmin,CN=Users,DC=myOffice,DC=myCompany,DC=de" search-credential="myLdapAdminPass"/>

          </outbound-connections>

          <management-interfaces>

              <native-interface security-realm="myCompany_ldap_realm">

                  <socket-binding native="management-native"/>

              </native-interface>

              <http-interface security-realm="myCompany_ldap_realm">

                  <socket-binding http="management-http"/>

              </http-interface>

          </management-interfaces>

      </management>

       

      <subsystem xmlns="urn:jboss:domain:security:1.1">

          <security-domains>

              <security-domain name="myCompany_ldap_domain">

                  <authentication>

                      <login-module code="org.jboss.security.auth.spi.LdapExtLoginModule" flag="required">

                          <module-option name="java.naming.provider.url" value="ldap://active-dir1.myOffice.myCompany.de:389/"/>

                          <module-option name="java.naming.security.authentication" value="simple"/>

                          <module-option name="bindDN" value="CN=myLdapAdmin,CN=Users,DC=myOffice,DC=myCompany,DC=de"/>

                          <module-option name="bindCredential" value="myLdapAdminPass"/>

                          <module-option name="baseCtxDN" value="CN=Users,DC=myOffice,DC=myCompany,DC=de"/>

                          <module-option name="baseFilter" value="(sAMAccountName={0})"/>

                          <module-option name="rolesCtxDN" value="CN=Users,DC=myOffice,DC=myCompany,DC=de"/>

                          <module-option name="roleFilter" value="(sAMAccountName={0})"/>

                          <module-option name="roleAttributeIsDN" value="true"/>

                          <module-option name="roleAttributeID" value="memberOf"/>

                          <module-option name="roleNameAttributeID" value="cn"/>

                          <module-option name="roleRecursion" value="-1"/>

                          <module-option name="allowEmptyPasswords" value="false"/>

                          <module-option name="throwValidateError" value="true"/>

                      </login-module>

                  </authentication>

              </security-domain>

          </security-domains>

      </subsystem>

      {code}

       

       

      WEB-INF/web.xml

      {code:xml}

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

      <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0">

          <display-name>jboss-as-sample-cdi-security</display-name>

       

          <security-constraint>

             <web-resource-collection>

               <web-resource-name>deny all except myLdapRole1</web-resource-name>

               <url-pattern>/*</url-pattern>

               <http-method>DELETE</http-method>

               <http-method>PUT</http-method>

               <http-method>HEAD</http-method>

               <http-method>OPTIONS</http-method>

               <http-method>TRACE</http-method>

               <http-method>GET</http-method>

               <http-method>POST</http-method>

             </web-resource-collection>

             <auth-constraint>

                <role-name>myLdapRole1</role-name>

             </auth-constraint>

             <user-data-constraint>

               <transport-guarantee>NONE</transport-guarantee>

             </user-data-constraint>

          </security-constraint> 

       

          <security-constraint>

             <display-name>exception: public access</display-name>

             <web-resource-collection>

                <web-resource-name>Public Access</web-resource-name>

                <url-pattern>/HelloCdi</url-pattern>

                <http-method>GET</http-method>

             </web-resource-collection>

             <user-data-constraint>

                <transport-guarantee>NONE</transport-guarantee>

             </user-data-constraint>

          </security-constraint>

       

          <security-role>

            <role-name>myLdapRole1</role-name>

          </security-role>

       

          <login-config>

            <auth-method>BASIC</auth-method>

            <realm-name>myCompany_ldap_domain</realm-name>

          </login-config>

      </web-app>

      {code}

       

       

      WEB-INF/jboss-web.xml

      {code:xml}

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

      <!DOCTYPE jboss-web PUBLIC "-//JBoss//DTD Web Application 5.0//EN" "http://www.jboss.org/j2ee/dtd/jboss-web_5_0.dtd">

      <jboss-web>

          <security-domain>java:/jaas/myCompany_ldap_domain</security-domain>

      </jboss-web>

      {code}

       

       

      WEB-INF/beans.xml

      {code:xml}

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

      <beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

          xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://docs.jboss.org/cdi/beans_1_0.xsd">

      </beans>

      {code}

       

       

      src/HelloService.java

       

      {code:java}

      // cdi object that will be injected to all servlets

      public class HelloService {

          String createHelloMessage(String name) {

              return "Hello " + name + "!";

          }

      }

      {code}

       

       

      src/HelloWorldServlet.java

      {code:java}

      // access via localhost:8080/jboss-as-sample-cdi-security/HelloCdi granted by security contraint "exception: public access" in web.xml

      @SuppressWarnings("serial")

      @WebServlet("/HelloCdi")

      public class HelloWorldServlet extends HttpServlet {

       

         static String PAGE_HEADER = "<html><head /><body>";

         static String PAGE_FOOTER = "</body></html>";

       

         @Inject

         HelloService helloService;

       

         @Override

         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            PrintWriter writer = resp.getWriter();

            writer.println(PAGE_HEADER);

            writer.println("<h1>" + helloService.createHelloMessage("Cdi") + "</h1>");

            writer.println(PAGE_FOOTER);

            writer.close();

         }

      }

      {code}

       

       

       

      src/HelloWorldServletSecure.java

      {code:java} 

      @SuppressWarnings("serial")

      @WebServlet("/HelloCdiSecure")

      // access via localhost:8080/jboss-as-sample-cdi-security/HelloCdiSecure granted by security contraint "deny all except myLdapRole1" in web.xml

      public class HelloWorldServletSecure extends HttpServlet {

       

         static String PAGE_HEADER = "<html><head /><body>";

         static String PAGE_FOOTER = "</body></html>";

       

         @Inject

         HelloService helloService;

       

         @Override

         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            PrintWriter writer = resp.getWriter();

            writer.println(PAGE_HEADER);

            writer.println("<h1>" + helloService.createHelloMessage("Cdi Secure") + "</h1>");

            writer.println(PAGE_FOOTER);

            writer.close();

         }

      }

      {code}

       

       

      This is how I would like it to be:

      Preferably a default configuration as is (i.e. a global rule in web.xml that maps to all urls/servlets resctricting usage to authenticated users)

      Plus an additional annotation for "special" servlets (e.g. admin / premium user functionality)

      Is there any way I can get this to work?

      Unfortunately all these annotations are ignored ...

       

       

      src/HelloCdiSecureAnnotation.java

      {code:java}

      @SuppressWarnings("serial")

      @WebServlet("/HelloCdiSecureAnnotation")

      @SecurityDomain("myCompany_ldap_domain") // this would be redundant if defined globally

      //@SecurityDomain("myCompany_ldap_domain2") // this could be an alterning security domain i maybe need for an external api

      @DenyAll // deny all users that match my global access exception

      @RolesAllowed(value = { "myLdapRole2" }) // allow special access roles

      public class HelloWorldServletSecureAnnotation extends HttpServlet {

       

         static String PAGE_HEADER = "<html><head /><body>";

         static String PAGE_FOOTER = "</body></html>";

       

         @Inject

         HelloService helloService;

       

         @Override

         protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

            PrintWriter writer = resp.getWriter();

            writer.println(PAGE_HEADER);

            writer.println("<h1>" + helloService.createHelloMessage("Cdi Secure Annotation") + "</h1>");

            writer.println(PAGE_FOOTER);

            writer.close();

         }

       

         @Override

         @RolesAllowed(value = { "myLdapRole3" })

         public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

             // do something

         }

      }

      {code}

       

       

       

      Compiled with:

      JDK 1.7.0_04

      JBoss 7.1.1 Runtime libraries

       

      Thanks to Pete Muir for the original sample project from jboss tools eclipse plugin: https://community.jboss.org/en/tools/blog/tags/deployment