Writing / Computers

Active Directory (AD) Authentication for JasperServer 3.x

Introduction

The new JasperServer 3 business intelligence (BI) and report server was released this month. And the first thing I attempted right after installing it was setting up Active Directory integration. As it turns out, AD integration is not an easy project, especially with the lack of documentation. After almost a week of intensive research, I finally figured out the essentials to get AD authentication working. I discuss AD authorization in a different article. In this article, I briefly document my findings to aid others in setting up AD authentication with JasperServer 3.

Background

The first important point to notice is that JasperServer authentication is based on Spring (formerly Acegis). Thus, LDAP authentication works, in theory, right out of the box. However, for it to work with Microsoft Active Directory, the Spring LDAP module requires some tweaking. All configuration changes are performed inside of two XML files under JasperServer's WEB-INF directory (more on this later).

JasperServer makes use of roles to provide access. The default roles are ROLE_ADMIN (used for administrators), ROLE_USER (used for regular users), and ROLE_ANONYMOUS (not really used by default). After a user is authenticated, his roles are checked against the database to determine what resources are accessible to him. In the case of LDAP, users are automatically added as new external users (i.e., users authenticated outside of the users table mechanism) upon a successful authentication. Moreover, they are assigned the ROLE_USER role by default, which might be undesirable.

In my test server, all the users are located under an organizational unit called BlogUsers. Thus, the complete path to my user ID would be: cn=gabriel,ou=BlogUsers,dc=mongefranco,dc=com. Moreover, I have different security groups setup under another OU, DepartmentGroups. My group would be: cn=Developers,ou=DepartmentGroups,ou=Groups,dc=mongefranco,dc=com.

Authentication Configuration

The first file to configure is ApplicationContext-security.xml, which is located in the WEB-INF directory. In the Authentication section, there is a bean called "authenticationManager." This bean contains a list of authentication providers, that is, plug-ins that provide access to different authentication protocols. When a user clicks Login on the home page, his credentials are authenticated by each of these providers, top to bottom, until a positive match is found. If none of the providers return a positive match, the user is denied access.

Since Active Directory uses the LDAP protocol, I enabled "ldapAuthenticationProvider." I also left "daoAuthenticationProvider" enabled so I could create local accounts that are not in AD, such as the jasperadmin administrative account. Also, for added security, I disabled the anonymousAuthenticationProvider." Thus, my authenticationManager bean looks like this:
\ <!-- ======================== AUTHENTICATION ======================= -->\ <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager">\ <property name="providers">\ <list>\ <ref local="ldapAuthenticationProvider"/>\ <ref local="daoAuthenticationProvider"/>\ <!--ref local="anonymousAuthenticationProvider"/-->\ <!--ref local="jaasAuthenticationProvider"/-->\ </list>\ </property>\ </bean>\

The next bean is "initialDirContextFactory," which sets up LDAP access for Spring. The first constructor-arg should contain only the Active Directory domain controller address, port number (usually 389), and the DC portion of the DN (do not include organizational units here!). The next two properties, "managerDN" and "managerPassword," are the user name and password of the account used to query Active Directory. In my test system, this is a service account used exclusively to query AD. The complete bean looks as follows:
\ <bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory">\ <constructor-arg value="ldap://ADSERVER:389/dc=mongefranco,dc=com"/>\ <property name="managerDn"><value>[the contact page](/contact/)</value></property>\ <property name="managerPassword"><value>password123!</value></property>\ </bean>\

After a user clicks Login on the home page, Spring first attempts to connect to AD using the settings above. After a successful connection, it re-binds using the user-supplied user name and password. In order to do this, it needs to know where and how to find the user. That is where the "userSearch" bean comes in. Composed of 3 constructors, it defines what attribute in AD contains the user name and also how to find it. In my system, the attribute is CN, so I put "cn={0}" where "{0}" is replaced by the user name entered by the user. The "searchSubtree" option will allow Spring to dig into sub-groups.
\ <bean id="userSearch" class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch">\ <constructor-arg index="0">\ <value></value>\ </constructor-arg>\ <constructor-arg index="1">\ <value>cn={0}</value>\ </constructor-arg>\ <constructor-arg index="2">\ <ref local="initialDirContextFactory" />\ </constructor-arg>\ <property name="searchSubtree">\ <value>true</value>\ </property>\ </bean>\

The next bean, ldapAuthenticationProvider, is a bit sensitive so you must be extra careful. It contains two other beans. The first has a "userDnPatterns" property used to find a user in the directory after Spring successfully binds to AD using the user name and password provided. If it cannot find the user, it immediately denies access. Since in my system all users are contained in the BlogUsers OU, I constructed the parameters as follows:
\ <constructor-arg>\ <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator">\ <constructor-arg><ref local="initialDirContextFactory"/></constructor-arg>\ <property name="userDnPatterns"><list>\ <value>cn={0},ou=BlogUsers</value>\ </list></property>\ </bean>\ </constructor-arg>\

The next bean is used for authorization and to map a user's AD group or OU to a JasperServer role. The following example shows how to configure mapping to the defaultRole property, which causes JasperServer to automatically assign this role to a user upon his first successful login. More details on authorization are discussed in another article.
\ <constructor-arg>\ <bean class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator">\ <constructor-arg index="0"><ref local="initialDirContextFactory"/></constructor-arg>\ <constructor-arg index="1"><value>ou=DepartmentGroups,ou=Groups</value></constructor-arg>\ <property name="groupRoleAttribute"><value>cn</value></property>\ <property name="groupSearchFilter"><value>((member={1})(cn=*))</value></property>\ <property name="searchSubtree"><value>true</value></property>\ <property name="rolePrefix"><value></value></property>\ <property name="defaultRole"><value>ROLE_ANONYMOUS</value></property>\ <property name="convertToUpperCase"><value>true</value></property>\ </bean>\ </constructor-arg>\

In addition to the defaultRole setup above, JasperServer also assigns a "ROLE_USER" which may be undesirable. At least in my case, I would not want every user to stumble upon the JasperServer URL, login and have access to all reports. So, after a couple of days of unsuccessful research, I simply searched for all references to "ROLE_USER" in all the XML files on the server. Eventually, I discovered that this second default role is configured in the ApplicationContext.xml file. After changing it to ROLE_ANONYMOUS, I denied access to every resource (including the root directory) to this role. That way, even though any user with an AD account can login, he will get an "Access Denied" message if he is not a member of an authorized OU.

Hopefully, my short experience with AD, LDAP, Spring and JasperServer will help the Open Source community. Believe me when I say that the lack of documentation in the subject is completely astonishing! With articles like these one, however, new users should be able to configure JasperServer to meet their needs. If you have also figured out how to do cool things with JasperServer, please consider writing about them to help other users. Feel free to link to your articles from the comments section below.

Back to blog