JAAS, Wildfly and Microsoft Active Directory

Some time ago, I wrote about a Java EE Web application that made use of Microsoft Active Directory, through Java Authentication and Authorization Service (JAAS), as its security mechanism. The program was deployed in a Glassfish 4.0 application server. I’ve recently moved this application to a Red Hat Wildfly 9.0.1 server and I’d like to share a couple of issues I consider can be helpful for those involved in the same topic.

The fist one is the configuration of the LDAP realm. Here you have an excerpt of my standalone-full.xml file:


<management>
  <security-realms>
  ...
    <security-realm name="LdapRealm">
      <authentication>
        <ldap connection="AdConnection" base-dn="OU=TestOU,DC=test,DC=local" recursive="true">
          <username-filter attribute="sAMAccountName"/>
        </ldap>
      </authentication>
      <authorization>
        <ldap connection="AdConnection">
          <group-search group-name-attribute="cn">
            <principal-to-group group-attribute="memberOf"/>
          </group-search>
        </ldap>
      </authorization>
    </security-realm>
  ...
  </security-realms>
  <outbound-connections>
    <ldap name="AdConnection" url="ldap://127.0.0.1:389" 
      search-dn="CN=testapp,CN=Users,DC=test,DC=local" search-credential="password"/>
  </outbound-connections>
  ...
</management>

The CLI script that creates this XML structure on Wildfly 9.0.1 is:


connect
/core-service=management/ldap-connection=AdConnection:add(url="ldap://127.0.0.1:389", \
  search-dn="CN=testapp,CN=Users,DC=test,DC=local",search-credential="password")
/core-service=management/security-realm=LdapRealm:add
/core-service=management/security-realm=LdapRealm/authentication=ldap:add(connection=AdConnection, \
  base-dn="OU=TestOU,DC=test,DC=local", \
  recursive="true", \
  username-attribute="sAMAccountName")
batch
/core-service=management/security-realm=LdapRealm/authorization=ldap:add(connection="AdConnection")
/core-service=management/security-realm=LdapRealm/authorization=ldap/ \
  group-search=principal-to-group:add(group-name-attribute="cn", \
  group-attribute="memberOf")
run-batch
reload

The second issue is the configuration of a security domain, which is a concept that Glassfish not requires, there you just set up the name of the realm in the login-config element of the file web.xml Wildfly ignores this configuration, but requires instead its specific jboss-web.xml file in the WEB-INF folder of the application:


<?xml version="1.0" encoding="UTF-8"?>
<jboss-web version="7.1"
  xmlns="http://www.jboss.com/xml/ns/javaee"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jboss.com/xml/ns/javaee http://www.jboss.org/schema/jbossas/jboss-web_7_1.xsd">
  <context-root>/ifaztelep/>context-root>
  <security-domain>test-domain/>security-domain>
</jboss-web>

Finally, here you have the CLI script that creates the security domain in Wildfly:


connect
/subsystem=security/security-domain=test-domain:add(cache-type="default")
/subsystem=security/security-domain=test-domain/authentication=classic:add \
 (login-modules=[{code="RealmDirect", \
  flag="required", \
  module-options={password-stacking="useFirstPass",realm="LdapRealm"}}])
reload

Advertisements

Setting up a JMS bridge between Weblogic and ActiveMQ

Almost four years ago, I wrote about how to setup a JMS bridge between Weblogic and HornetQ. Lately, I’ve had to research how to do the same work with ActiveMQ. Here you have my findings. As it was for HornetQ, the first step was to copy the client libraries to a folder residing in a filesystem of my Weblogic server, in the case of ActiveMQ these files are:

  • activemq-client-5.10.0.jar
  • hawtbuf-1.10.jar
  • slf4j-api-1.7.5.jar

Later on, I set the PRE_CLASSPATH variable pointing it to these libraries into the script setDomainEnv. Creating messaging destinations is more or less the same job as it was for HornetQ, you just have to change the initial context factory and connection url parameters:

<jms-bridge-destination>
  <name>JMS Bridge Destination-Target</name>
  <adapter-jndi-name>eis.jms.WLSConnectionFactoryJNDIXA</adapter-jndi-name>
  <user-name>testUser</user-name>
  <user-password-encrypted>{AES}GZTa15osrQW6/5rbE2fzm+a9BX/YPY7Hm3xiIFi0oMQ=</user-password-encrypted>
  <classpath></classpath>
  <connection-factory-jndi-name>jms/TestXAQueueConnectionFactory</connection-factory-jndi-name>
  <initial-context-factory>org.apache.activemq.jndi.ActiveMQInitialContextFactory</initial-context-factory>
  <connection-url>tcp://localhost:61616</connection-url>
  <destination-jndi-name>jms/TestQueue</destination-jndi-name>
  <destination-type>Queue</destination-type>
</jms-bridge-destination>

But the former configuration is not enough, because ActiveMQ hasn’t his own JNDI provider (I had ActiveMQ as a JMS provider for a ServiceMix ESB) and requires a jndi.properties file with the mappings between physical destinations and tha jndi ones, but, how to configure the properties file in the context of a Weblogic messaging bridge? Here is the trick: create a JAR archive containing the jndi.properties file and put the JAR in the CLASSPATH (the same way at it was described before for activemq-client-5.10.0.jar). The steps to create the JAR archive are:

  • Create a file called jndi.properties with the following entries:

connectionFactoryNames=ConnectionFactory,XAConnectionFactory queue.TestQueue=TestQueue

  • Create the archive with the following command:

jar cvf jndi.jar jndi.properties

I found this trick in this post, after have tested many different configurations, including the setup of a Weblogic Foreign JMS Server, without success. Finally, I’d like to point out that I am not completely happy with this setup because it has an obvious drawback: you have modify and redeploy the jndi.jar archive every time you add a new queue or topic, so suggestions are welcome!!!


References


SQL Server data sources in JBoss AS 7

Last week, I set up a SQL Server 2008 data source on a JBoss AS 7.1.1 server, in order to be used by a Java EE application, so I’d like to share what I’ve learned.

The first step was to install the driver. There are two ways to do this, the quick one is simply to deploy the jdbc driver (sqljdbc4.jar) as a regular deployment, by typing this command in CLI (the name parameter is optional, but I found it useful):

deploy C:\software\drivers\sqljdbc4.jar –name=sqlserver

The second option is to install the jdbc driver as a core module, which it was what I finally did. This one was a bit more laborious. First of all, I turned off the server and I set up a directory structure under JBoss modules folder, in my case C:\jboss-as-7.1.1.Final\modules\com\microsoft\sqlserver\main, after that, I copied the driver sqljdbc4.jar there and I created a file called module.xml with the following content:

  <?xml version="1.0" encoding="UTF-8"?>
  <module xmlns="urn:jboss:module:1.0" name="com.microsoft.sqlserver">
    <resources>
      <resource-root path="sqljdbc4.jar"/>
    </resources>
    <dependencies>
      <module name="javax.api"/>
      <module name="javax.transaction.api"/>
    </dependencies>
  </module>

The key here is to create a directory structure that matches the module name. The final step of this option was to start the server and run the following CLI command:

/subsystem=datasources/jdbc-driver=sqlserver:add(driver-name=sqlserver,driver-module-name=com.microsoft.sqlserver,driver-xa-datasource-class-name=com.microsoft.sqlserver.jdbc.SQLServerXADataSource)

Once I had the driver configured, I created the data source by using this CLI command (for this sample, I set up a local SQL Server EXPRESS instance, with a test database and a test user):

data-source add –name=TestDS –jndi-name=java:/jdbc/Test –driver-name=sqlserver –connection-url=jdbc:sqlserver://localhost\SQLEXPRESS;databaseName=Test –user-name=test –password=test –min-pool-size=10 –max-pool-size=50 –pool-use-strict-min=true –pool-prefill=true –jta=true –use-ccm=true –prepared-statements-cache-size=32

 The data source has to be enabled:

data-source enable –name=TestDS

Finally, I tested the configuration with the following command:

/subsystem=datasources/data-source=TestDS/:test-connection-in-pool

Creating a XA data source was slightly different. The first step was to check out that my SQL Server installation was properly configured, by reviewing the chapter titled Configuration Instructions of this article. After that, I ran these commands:

xa-data-source add –name=TestDS–jndi-name=java:/jdbc/Test/XA –driver-name=sqlserver –user-name=test –password=test–min-pool-size=10 –max-pool-size=50 –pool-use-strict-min=true –pool-prefill=true –jta=true –use-ccm=true –prepared-statements-cache-size=32 –same-rm-override=false
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=ServerName:add(value=localhost\SQLEXPRESS)
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=DatabaseName:add(value=Test)
/subsystem=datasources/xa-data-source=TestDS/xa-datasource-properties=SelectMethod:add(value=cursor)
xa-data-source enable –name=TestDS
/subsystem=datasources/xa-data-source=TestDS/:test-connection-in-pool

A final tip, if you decide to deploy the driver, instead of installing it as a core module, you have to add the parameter –xa-datasource-class to the command xa-data-source add with the value com.microsoft.sqlserver.jdbc.SQLServerXADataSource


References:


JAAS, Glassfish and Microsoft Active Directory

I recently had to develop an internal Java EE Web application that made use of Microsoft Active Directory, through Java Authentication and Authorization Service (JAAS), as its security mechanism. The program must be deployed in a Glassfish 4.0 application server.

I don’t want to write the same post that other people have written before, so here you have a link to a tutorial written by Marcel Gascoyne, who explains clearly the setup that it is needed. The reason why I’m writing about this issue is because I had to make a change in Marcel’s configuration: with the JVM option -Djava.naming.referral=follow, my system didn’t retrieve the groups membership of the user authenticated, I put the option as a LDAP realm property instead. Once more stackoverflow.com was key to solve the problem.

Finally, I’d like to comment that I couldn’t setup the LDAP realm through Glassfish Web admin console because I got sintax errors with the character “=”, so I had to modify the file domain.xml directly. Another question is how to enable logging on the security system, it’s easy in theory but I couldn’t do it. I found this thread in stackoverflow.com, but I didn’t figure out the logger name.


JPA embedded sample

During my last talk on Betabeers, titled “Introduction to Java EE”, I was asked if JPA and Bean Validation could be used in stand-alone applications. I had seen some examples of those utilizations, but, to be perfectly honest, I had never used, so I’ve written a small sample that shows how to use JPA and Bean Validation in that context. Please, follow this link to review my source code.


Java EE sample

Last Thursday, March 27th, 2014, I offered a talk called “Introduction to Java EE”, as part of our local developers group’s event, Betabeers. It was an amazing experience for me.

20140327BetabeersI started off with a short theoretical introduction. Later on, I briefly showed some of the API of Java EE through a small CRUD sample application for registering ship data. I talked about JPA, EJB, Bean Validation, JSF, Managed Beans, JAX-RS and JAAS.

Here you have the PowerPoint that supported my presentation:


JasperReports Server & CAS Authentication

I discovered Central Authentication Service (CAS) through an old copy of the “JasperServer External Authentication Cookbook”, about two years ago. Therefore, CAS was my first option when I was appointed to evaluate open source single-sign on products, and, of course, JasperReports Server was the first Web application to try it. Learning how to configure and deploy was relatively easy using the CAS User Manual, but the set up of JasperServer was a nightmare because the lack of documentation of the community version, that’s why I’m writing this post!

First of all, I set up a virtual machine with CAS configured to use an Active Directory user authentication, the tutorial “End-to-end Windows Example” of the CAS wiki was very useful at this point. Later on, I deployed JasperReports Server to another virtual machine. The communication between these two machines is through SSL, so I had to export the certificate used by CAS one and import into the trust store of the JasperServer one, the key points here were:

  1. Put the fully qualified domain name (FQDN) of CAS machine as the CN of the certificate.
  2. Register that FQDN into the DNS used by JasperServer machine.
  3. Import the certificate into the cacerts file of the JVM where runs the application server where JasperServer Web application is deployed.

Configuring JasperReports Server v5.2.0 was a matter of researching on forums and blogs, intuition, trial and error. Finally, the successful changes on the original JasperServer configuration were:

  • Adding the following beans to applicationContext-security.xml:

      <bean id="casAuthenticationProvider"
          class="org.springframework.security.providers.cas.CasAuthenticationProvider">
        <property name="userDetailsService"><ref local="casUserAuthorityService"/></property>
        <property name="serviceProperties"><ref local="authenticationServiceProperties"/></property>
        <property name="ticketValidator">
            <bean class="org.jasig.cas.client.validation.Cas20ServiceTicketValidator">
                <constructor-arg index="0" value="https://cas.test.com/cas" />
            </bean>
        </property>
        <property name="statelessTicketCache">
            <bean class="org.springframework.security.providers.cas.cache.EhCacheBasedTicketCache">
                <property name="cache"><ref local="ticketCache"/></property>
            </bean>
        </property>
        <property name="key"><value>lam_or_lame</value></property>
    </bean>

    <bean id="authenticationServiceProperties"
          class="org.springframework.security.ui.cas.ServiceProperties">
        <property name="service">
           <value>http://jasperserver.test.com/jasperserver/j_spring_cas_security_check</value>
        </property>
        <property name="sendRenew"><value>false</value></property>
    </bean>

    <bean id="ticketCache" class="org.springframework.cache.ehcache.EhCacheFactoryBean">
       <property name="cacheManager"><ref local="cacheManager"/></property>
       <property name="cacheName"><value>casTicketCache</value></property>
    </bean>

    <bean id="casUserAuthorityService"
          class="com.jaspersoft.jasperserver.api.metadata.user.service.impl.UserDetailsServiceImpl">
        <property name="adminUsers">
            <list>
                <value>fcosfc</value>
            </list>
        </property>
        <property name="defaultAdminRoles">
            <list>
                <value>ROLE_USER</value>
                <value>ROLE_ADMINISTRATOR</value>
            </list>
        </property>
        <property name="defaultInternalRoles">
            <list>
                <value>ROLE_USER</value>
            </list>
        </property>
    </bean>
  • Adding a new Authentication provider to applicationContext-security.xml:

    <bean id="authenticationManager" class="org.springframework.security.providers.ProviderManager">
        <property name="providers">
            <list>
                <ref local="casAuthenticationProvider"/>

                ...

            </list>
        </property>
    </bean>
  • Replacing the beans authenticationProcessingFilter and authenticationProcessingFilterEntryPoint in applicationContext-security.xml:

     <bean id="authenticationProcessingFilter"
          class="org.springframework.security.ui.cas.CasProcessingFilter">
        <property name="authenticationManager"><ref local="authenticationManager"/></property>
        <property name="authenticationFailureUrl"><value>/loginerror.html</value></property>
        <property name="defaultTargetUrl"><value>/loginsuccess.html</value></property>
        <property name="filterProcessesUrl"><value>/j_spring_cas_security_check</value></property>
    </bean>

    ...

    <bean id="authenticationProcessingFilterEntryPoint"
          class="org.springframework.security.ui.cas.CasProcessingFilterEntryPoint">
        <property name="loginUrl"><value>https://cas.test.com/cas/login</value></property>
        <property name="serviceProperties"><ref local="authenticationServiceProperties"/></property>
    </bean>
  • Modifying the filter chain in applicationContext-security-web.xml:

    <bean id="filterChainProxy" class="org.springframework.security.util.FilterChainProxy">
        <property name="filterInvocationDefinitionSource">
            <value>
                CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON
                PATTERN_TYPE_APACHE_ANT
                ...
                /j_spring_cas_security_check=httpSessionContextIntegrationFilter,
authenticationProcessingFilter,anonymousProcessingFilter,
exceptionTranslationFilter,filterInvocationInterceptor
                ...
                /**=httpSessionContextIntegrationFilter, ...,
delegatingRequestParameterAuthenticationFilter,JIAuthenticationSynchronizer,
anonymousProcessingFilter,...
            </value>
        </property>
    </bean>

References