Thursday 27 October 2011

How to: interface JasperReports Server and LDAP (2/2)

Multi-tenancy option


As predefined option, JasperReports Server maps all external users inside organization_1. If you want to simply change the destined organization:

  • Open the file /jasperserver-pro/WEB-INF/applicationContext-multiTenancy-security.xml
  • Locate the bean defaultExternalUserProcessor
  • Modify the property defaultOrganizationIfNotDetected (as shown below)
<bean id="defaultExternalUserProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.DefaultExternalUserProcessor">
<property name="multiTenancyService"><ref bean="internalMultiTenancyService"/></property>
<property name="defaultOrganizationIfNotDetected" value="organization_1"/>
        <property name="multiTenancyConfiguration"><ref bean="multiTenancyConfiguration"/></property>
        <property name="rootOrganizationRolesMap">
            <map>
                <!-- Mapping customers roles to JS roles Example -->
                <!--<entry>-->
                    <!--<key>-->
                       <!-- Сustomer role(with adding ROLE_ prefix) which need to be mapped to root JS roles -->
                       <!--<value>ROLE_ADMIN</value>-->
                    <!--</key>-->
                       <!-- root JS role customer role to be mapped to -->
                       <!--<value>ROLE_ADMINISTRATOR</value>-->
                <!--</entry>-->
            </map>
        </property>
</bean>

WARNING: if you don't specify anything in defaultOrganizationIfNotDetected, the external users will be mapped inside the root folder and will have the same visibility as superuser!
If you want instead to map on JasperReports Server the LDAP directory structure we have to proceed as follows:

  • Open the file /jasperserver-pro/WEB-INF/applicationContext-multiTenancy-security.xml
  • Locate the bean mtUserAuthorityServiceTarget
  • Locate the property userProcessors
  • Uncomment the ldapExternalUserProcessor reference and comment defaultExternalUserProcessor
<bean id="mtUserAuthorityServiceTarget"
        class="com.jaspersoft.jasperserver.multipleTenancy.MTUserAuthorityServiceImpl">
        <property name="sessionFactory" ref="sessionFactory"/>
        <property name="objectMappingFactory" ref="mappingResourceFactory"/>
        <property name="persistentClassFactory" ref="persistentMappings"/>
        <property name="profileAttributeService" ref="profileAttributeService"/>
        <property name="defaultInternalRoles">
          <list>
            <value>ROLE_USER</value>
          </list>
        </property>
        <property name="multiTenancyConfiguration"><ref bean="multiTenancyConfiguration"/></property>
        <property name="securityProvider"><ref local="tenantSecurityProvider"/></property>
        <property name="securityContextProvider"><ref bean="${bean.securityContextProvider}"/></property>
        <property name="tenantPersistenceResolver"><ref bean="${bean.hibernateTenantService}"/></property>
        <property name="userProcessors">
            <list>
                <!-- For LDAP authentication -->
                <ref bean="ldapExternalUserProcessor"/>
                <!--ref bean="defaultExternalUserProcessor"/-->
            </list>
        </property>
        <property name="auditContext" ref="${bean.auditContext}"/>
        <property name="databaseCharactersEscapeResolver" ref="databaseCharactersEscapeResolver"/>
        <property name="defaultExternalUserProcessor" ref="defaultExternalUserProcessor"/>
</bean>


  • Find the bean ldapExternalUserProcessor and uncomment it
  • Add the following informations:
    • excludeRootDN: specify if the baseDN (inside the connection parameters) should be mapped with the RDN (in our example, if the folders com and maxcrc should be created or not)
    • organizationsRDN: a list of attributes which will be mapped as organization names. If this list is empty or there is no match between this list and the LDAP directory, the organization name will be determined solely by the excludeRootDN and rootOrganizationId properties
    • rootOrganizationId: the organizationId under which the organizations mapped from the LDAP directory will be created. If empty, it's root
    • rootOrganizationRolesMap: a useful property if you want to map JasperReports Server system roles with the ones defined in LDAP (see below)
<!-- For LDAP authentication -->
<bean id="ldapExternalUserProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.ldap.LdapExternalUserProcessor">
<property name="ldapContextSource" ref="ldapContextSource"/>
<property name="multiTenancyService"><ref bean="internalMultiTenancyService"/></property>
<property name="excludeRootDn" value="true"/>
<!-- only following RDNs will matter in creating of organization hierarchy -->
<property name="organizationRDNs">
<list>
<!--<value>dc</value>-->
<!--<value>c</value>-->
<value>ou</value>
<value>o</value>
<!--<value>st</value>-->
</list>
</property>
<property name="rootOrganizationId" value=""/>
        <property name="multiTenancyConfiguration"><ref bean="multiTenancyConfiguration"/></property>
        <property name="rootOrganizationRolesMap">
            <map>
            </map>
        </property>
</bean>

In our example:

  • excludeRootDN: true (we don't want to create com and maxcrc folders)
  • organizationsRDN: o, ou (we want to map a first-level organization named People and two second-level organizations: jobs and users)
  • rootOrganizationId: null (we want People to be a first-level organization)
WARNING(1): if we have rootOrganizationId = null, excludeRootDN = true and no match between the attributes in organizationsRDN and the LDAP directory structure, the external users will be mapped inside the root folder and will have the same visibility as superuser!
WARNING(2): it's necessary to create beforehand the People folder: jobs and users will be created at the first login of either paul or tom

Role Mapping


In order to find user roles, LDAP makes a second search inside the directory structure: in order to specify search parameters, we have to proceed as follows:

  • Open the file /jasperserver-pro/WEB-INF/applicationContext-security.xml
  • Locate the bean ldapAuthenticationProvider
  • In the second constructor add the following informations:
    • constructor-arg index="1": a branch of the LDAP directory where the roles are defined. If empty, LDAP will cover the entire directory tree
    • groupRoleAttribute: the attribute whose value is the role name to map
    • groupSearchFilter: a filter search. In this case, the expression {0} is the complete DN of the username (ex: cn=tom,o=users,ou=People,dc=maxcrc,dc=com), while the expression {1} is the username specified in the login page (ex: tom)
    • searchSubTree: a flag that indicates whether or not the search should extend to eventual children nodes
    • convertToUpperCase: a flag that indicates whether the group name should be converted to upper case when used as a role name in JasperReports Server (the default is true)
    • rolePrefix: a prefix to the group name to add when creating a Jasper role (the default is ROLE_)
To all users will be assigned the predefined role ROLE_USER at the first access.



<!-- For LDAP authentication -->
   
   <bean id="ldapAuthenticationProvider" class="org.springframework.security.providers.ldap.LdapAuthenticationProvider">
     <constructor-arg>
       <bean class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
          <constructor-arg><ref local="ldapContextSource"/></constructor-arg>
          <!-- property name="userDnPatterns"><list><value>uid={0}</value></list></property -->
          <property name="userSearch" ref="userSearch"/>
       </bean>
     </constructor-arg>
     <constructor-arg>
       <bean class="org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator">
          <constructor-arg index="0"><ref local="ldapContextSource"/></constructor-arg>
          <constructor-arg index="1"><value>o=group,ou=departmentGroups</value></constructor-arg>
          <property name="groupRoleAttribute"><value>cn</value></property>
          <!--<property name="groupSearchFilter"><value>
(&amp;(uniqueMember={0})(objectclass=groupOfUniqueNames))</value></property>-->
          <property name="searchSubtree"><value>false</value></property>            
       </bean>
     </constructor-arg>
   </bean>


In our example:

  • constructor-arg index="1": o=group, ou=departmentGroups
  • groupRoleAttribute: cn
  • groupSearchFilter: nothing (JasperReports will user the member attribute as a filter if nothing is specified)
  • searchSubTree: false
System Role Mapping


Once you have tested that JasperReports Server creates the roles defined externally in the LDAP directory, it's possible to map these roles to the predefined system ones in JasperReports Server: for example, in our case we want to associate ROLE_DEVELOPER to ROLE_ADMINISTRATOR.
WARNING: with this procedure user paul won't have the ROLE_DEVELOPER anymore, just ROLE_ADMINISTRATOR; therefore, it's best to delete ROLE_DEVELOPER or user paul entirely if already present, in order to avoid Jasper launching an IllegalArgumentException.

  • Open the file /jasperserver-pro/WEB-INF/applicationContext-multiTenancy-security.xml
  • Locate the bean defaultExternalUserProcessor (if you are mapping the users inside a single organization) or ldapExternalUserProcessor (if you are using multi-Tenancy)
  • Locate the property rootOrganizationRolesMap and map the LDAP role to the corresponding Jasper role, as shown below



<!-- For LDAP authentication -->
<bean id="ldapExternalUserProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.ldap.LdapExternalUserProcessor">
<property name="ldapContextSource" ref="ldapContextSource"/>
<property name="multiTenancyService"><ref bean="internalMultiTenancyService"/></property>
<property name="excludeRootDn" value="true"/>
<!-- only following RDNs will matter in creating of organization hierarchy -->
<property name="organizationRDNs">
<list>
<!--<value>dc</value>-->
<!--<value>c</value>-->
<value>ou</value>
<value>o</value>
<!--<value>st</value>-->
</list>
</property>
<property name="rootOrganizationId" value=""/>
        <property name="multiTenancyConfiguration"><ref bean="multiTenancyConfiguration"/></property>
        <property name="rootOrganizationRolesMap">
            <map>
<entry>
<key>
<value>ROLE_DEVELOPER</value>
</key>
<value>ROLE_ADMINISTRATOR</value>
</entry>
            </map>
        </property>
</bean>





1 comment:

  1. Hi,

    Does jaspersoft allows multiple instances of LDAP?

    Please suggest.

    Thanks
    Gowtham

    ReplyDelete