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 & Infor PM OLAP

I’ve been working with Infor PM OLAP Server for almost four years now, I knew it when it was known as MIS Alea, before Infor took over MIS AG. It is one of those products you don’t select, a client has it and you have to work with it. I’ve always used Infor products, like Infor Office Plus and Infor Application Studio, in order to access to this OLAP database, but, last week, I tried to do it using my favourite reporting platform: JasperReports Server, so I’d like to share my findings.

First of all, i’d like to point out that Jaspersoft hasn’t certified this OLAP DB  as a source of data. It is obvious, but I must say that I just want to tell you my experience, so I am not responsible for any misfunction caused, if you try to do what I did.

Infor PM OLAP Server (10.1.7 version, in my case) is tightly coupled to Microsoft Windows. Therefore, the interaction with JasperReports has to run through a standard, in this case XML for Analysis (XMLA). It is difficult to find information about Infor PM OLAP products on the Internet, so I relied on the documentation of the DVD which has the software, specially the “Provider for Infor PM OLAP” manual.

The default installation of Infor had the XML interface up and running. I just had to change the name of the server in the file C:\Program Files\Infor PM\OLAP\XMLA\DataSources.xml, I replaced the default host, localhost, by the name of my server (for this post inforserver), I also enabled the Basic Authentication on the site InforPMOLAPXmla. After I restarted IIS, I browsed through the URL of the interface: http://inforserver/InforPMOlapXMLA/XmlaWebService.asmx

Later on, I started to work with iReport by creating an OLAP connection (option XMLA Server of the datasources dialog). I could test the connection and get the metadata I needed: datasource, catalog and cube. The problems began when I tried to execute a MDX query through iReport, I didn’t get an error but nothing happened.  I searched for a solution on the Internet and I realized that you have to perform the fields mappings manually, the JasperReports Ultimate Guide was very helpful at this point. After an effort, I had my first test report and, of course, it didn’t run:


Error filling print... XML/A fault: The server did not recognize the value of the header 
HTTP SOAPAction: "urn:schemas-microsoft-com:xml-analysis:Execute".; Code: Client;
net.sf.jasperreports.engine.JRRuntimeException: XML/A fault: The server did not recognize the 
value of the header HTTP SOAPAction: "urn:schemas-microsoft-com:xml-analysis:Execute".; Code: Client;
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.handleResultFault(JRXmlaQueryExecuter.java:494)
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.parseResult(JRXmlaQueryExecuter.java:335)
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.createDatasource(JRXmlaQueryExecuter.java:130)
at net.sf.jasperreports.engine.fill.JRFillDataset.createQueryDatasource(JRFillDataset.java:1112)
at net.sf.jasperreports.engine.fill.JRFillDataset.initDatasource(JRFillDataset.java:689)
at net.sf.jasperreports.engine.fill.JRBaseFiller.setParameters(JRBaseFiller.java:1281)
at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:900)
at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:152)
at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:464)
at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:300)
at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:757)

I researched the issue and, finally, I had to modify the source code (version 5.2.0) of the class net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter I changed the line 222 of the method createQueryMessage:


mh.setHeader("SOAPAction", "\"urn:schemas-microsoft-com:xml-analysis:Execute\"");

to


mh.setHeader("SOAPAction", "urn:schemas-microsoft-com:xml-analysis:Execute");

This is the great advantage of the open source! I compiled the source code, took the file jasperreports-5.2.0 and changed the one shipped with iReport. I tried a preview of my report and, ufs, another exception:


net.sf.jasperreports.engine.JRRuntimeException: Could not find dimension "MONTH" on axis 1.
at net.sf.jasperreports.olap.JROlapDataSource.getDimensionIndex(JROlapDataSource.java:451)
at net.sf.jasperreports.olap.mapping.MappingParser.getDimensionIndex(MappingParser.java:75)
at net.sf.jasperreports.olap.mapping.MappingParser.tuplePosition(MappingParser.java:363)
at net.sf.jasperreports.olap.mapping.MappingParser.member(MappingParser.java:263)
at net.sf.jasperreports.olap.mapping.MappingParser.memberMapping(MappingParser.java:163)
at net.sf.jasperreports.olap.mapping.MappingParser.mapping(MappingParser.java:133)
at net.sf.jasperreports.olap.JROlapDataSource.init(JROlapDataSource.java:322)
at net.sf.jasperreports.olap.JROlapDataSource.(JROlapDataSource.java:122)
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.createDatasource(JRXmlaQueryExecuter.java:141)
at net.sf.jasperreports.engine.fill.JRFillDataset.createQueryDatasource(JRFillDataset.java:1112)
at net.sf.jasperreports.engine.fill.JRFillDataset.initDatasource(JRFillDataset.java:689)
at net.sf.jasperreports.engine.fill.JRBaseFiller.setParameters(JRBaseFiller.java:1281)
at net.sf.jasperreports.engine.fill.JRBaseFiller.fill(JRBaseFiller.java:900)
at net.sf.jasperreports.engine.fill.JRFiller.fill(JRFiller.java:152)
at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:464)
at net.sf.jasperreports.engine.JasperFillManager.fill(JasperFillManager.java:300)
at net.sf.jasperreports.engine.JasperFillManager.fillReport(JasperFillManager.java:757)

This time it was a bit more difficult to find the error, I had to debug and I found that Infor PM return the dimension name between [], so I modified the class net.sf.jasperreports.olap.JROlapDataSource, changing the line 431 of the method getDimensionIndex


if (dimension.equals(hierarchy.getDimensionName()))

to


if (dimension.equals(hierarchy.getDimensionName()) || 
    (("[" + dimension + "]").equals(hierarchy.getDimensionName())))

Once I compiled and changed the file on iReport, I could run the report properly.

The next step was to move the report to JasperServer. The first task here was to replace the default jasperreports-5.2.0.jar  by the one I modified. After that, I restarted JasperReports Server. When I tried to run the report, I got the following exception:



net.sf.jasperreports.engine.JRRuntimeException: Message-Call failed. 
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.executeQuery(JRXmlaQueryExecuter.java:310) 
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.createDatasource(JRXmlaQueryExecuter.java:127) 
...
Caused by: com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl:
 java.security.PrivilegedActionException:
 com.sun.xml.internal.messaging.saaj.SOAPExceptionImpl:
 Bad response: (401Unauthorized at com.sun.xml.internal.messaging.saaj.client.p2p.HttpSOAPConnection.call(HttpSOAPConnection.java:157) 
at net.sf.jasperreports.olap.xmla.JRXmlaQueryExecuter.executeQuery(JRXmlaQueryExecuter.java:306) ... 16 more

It was exasperating! According to this forum post, the cause rely on the JVM, so I adapted me to the circumstances and revert the first change I made on the source code (“\”urn:schemas-microsoft-com:xml-analysis:Execute\””). I know have two different versions of the file jasperreports-5.2.0.jar: one for iReport and one for JasperServer.


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


Upgrading JasperReports Server

I’ve just upgraded my JasperServer installation from version 4.2.1 to 5.2.0 I followed the chapter 9 of the JasperReports Server community project installation guide, release 5.2.0, all ran properly and I logged on using the user jasperadmin. The former version used to authenticate its users connecting to a Microsoft Active Directory, the manual says at this point:

“Configuration modifications, such as client-specific security classes or LDAP server configurations, need to be hand-copied from your previous environment and re-integrated into the upgraded environment”

So, I copied the LDAP server configuration I described on my post JasperServer user authentication with Microsoft Active Directory, I restarted tomcat and tried to log on using my Active Directory credentials, but the access was denied. I reviewed the server log and I read the following exception:

org.springframework.beans.NotReadablePropertyException: Invalid property ‘principal.fullName’ of bean class [org.springframework.security.providers.UsernamePasswordAuthenticationToken]: Bean property ‘principal.fullName’ is not readable
or has an invalid getter method: Does the return type of the getter match the parameter type of the setter?
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:729)
at org.springframework.beans.BeanWrapperImpl.getPropertyValue(BeanWrapperImpl.java:721)

After researching on the Internet, I edited the file applicationContext-security-web.xml and added the filter in bold:

/**=httpSessionContextIntegrationFilter,encryptionFilter,multipartRequestWrapperFilter,
webAppSecurityFilter,jsCsrfGuardFilter,${bean.loggingFilter},${bean.userPreferencesFilter},
delegatingAuthenticationProcessingFilter,${bean.userPreferencesFilter},${bean.basicProcessingFilter},
delegatingRequestParameterAuthenticationFilter,JIAuthenticationSynchronizer,
anonymousProcessingFilter,delegatingExceptionTranslationFilter,filterInvocationInterceptor,
switchUserProcessingFilter,iPadSupportFilter

I restarted the application server and Active Directory authentication became available again.


References


How to assemble an standalone application using Maven

I’m a newbie at Maven, but I’m becoming a fan. Today I’d like to talk about a simple task, which Netbeans performs automatically in Ant based standalone project but doesn’t in Maven ones, assembling the library dependencies in a lib folder along with the jar file:

JMSBasicClientScreenshot

First of all, I had to add the Maven Dependency plugin to my pom.xml file, this action created a folder called dependency in my Maven target directory when a built the project:


<plugin>
  <artifactId>maven-dependency-plugin</artifactId>
  <executions>
    <execution>
      <phase>process-sources</phase>
      <goals>
        <goal>copy-dependencies</goal>
      </goals>
      <configuration>
        <outputDirectory>${targetdirectory}</outputDirectory>
      </configuration>
    </execution>
  </executions>
</plugin>

Later on, I also put the Maven JAR plugin with a configuration in order to add the CLASSPATH to the manifest file, using the same prefix (lib) I wanted to use in the assembly:


<plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-jar-plugin</artifactId>
  <configuration>
    <archive>
      <manifest>
        <addClasspath>true</addClasspath>                            
        <classpathPrefix>lib/</classpathPrefix>                            
        <mainClass>com.wordpress.fcosfc.JMSBasicClient</mainClass>
      </manifest>
     </archive>
  </configuration>
</plugin>

The following task was writing a custom assembly descriptor for the Maven Assembly plugin and putting in the folder src/main/assembly:


...
<id>bin</id>
<formats>
  <format>zip</format>
</formats>
<fileSets>
  <fileSet>
    <directory>${project.build.directory}</directory>
    <outputDirectory>/</outputDirectory>
    <includes>
      <include>*.jar</include>
    </includes>
  </fileSet>
  <fileSet>
    <directory>${project.build.directory}/dependency</directory>
    <outputDirectory>lib</outputDirectory>
  </fileSet>
</fileSets>
...

Finally, I had to add the Maven Assembly plugin to my pom.xml file:


<plugin>
  <artifactId>maven-assembly-plugin</artifactId>
  <version>2.4</version>
  <configuration>
    <descriptors>
      <descriptor>src/main/assembly/customAssembly.xml</descriptor>
    </descriptors>
  </configuration>
  <executions>
    <execution>
      <id>make-assembly</id> 
      <phase>package</phase> 
      <goals>
        <goal>single</goal>
      </goals>
    </execution>
  </executions>
</plugin>

References