LDAP (AD 2012) Nested AD Groups and Role Mapping

0

JasperReports 6.3.0 CE (WAR distro on Tomcat7 JDK7)

After a considerable amount of reading documentation, cookbooks, and community posts, I can officially say this has been one of the most esoteric LDAP integrations I've ever performed.  I'm currently trying to get jasper to map internal roles to users who are not diretly members of role mapped AD groups.  I appreciate your time and hope that someone with more direct experience with spring and LDAP (which I'm currently reading) can provide an easy fix or at least something to guide me down the right path.  As with every detail of this configuration I'm sure it's something small but impactful.

I'm currently trying to get user roles assigned when authenticated users are members of nested groups in AD.  Only users who are members of groups within the Jasper* group structure should be authenticated.

For example our test user:

Test User (tuser@ihazthadumb.com) (CN=Test User,OU=Test Accounts,OU=Service Accounts,OU=O365 Synced,DC=ihazthadumb,DC=com)

Is a member of

ihtd - IM (ihtd-im@ihazthadumb.com,) (CN=ihtd - IM,OU=Groups,OU=O365 Synced,DC=ihazthadumb,DC=com)

Which is a member of

Jasper_IM (no smtp) (CN=Jasper_IM,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com)

which is a member of

Jasper_Users (no smtp) (CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com)

Authentication is working. As is authorization (via role mapping) when the user is directly a member of the mapped role group.

Searching for user 'tuser@ihtd.com', with user search [ searchFilter: '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))', searchBase: '', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2017-04-24 12:27:34,520 DEBUG FilterBasedLdapUserSearch,http-nio-8443-exec-12:107 - Searching for user 'tuser@ihtd.com', with user search [ searchFilter: '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))', searchBase: '', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2017-04-24 12:27:34,979 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:211 - Searching for entry under DN 'dc=ihazthadumb,dc=com', base = '', filter = '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))'
2017-04-24 12:27:34,979 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:211 - Searching for entry under DN 'dc=ihazthadumb,dc=com', base = '', filter = '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))'
2017-04-24 12:27:34,984 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:223 - Found DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced
2017-04-24 12:27:34,984 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:223 - Found DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced
2017-04-24 12:27:35,360 DEBUG BindAuthenticator,http-nio-8443-exec-12:108 - Attempting to bind as cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:27:35,360 DEBUG BindAuthenticator,http-nio-8443-exec-12:108 - Attempting to bind as cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:27:35,434 DEBUG BindAuthenticator,http-nio-8443-exec-12:116 - Retrieving attributes...
2017-04-24 12:27:35,434 DEBUG BindAuthenticator,http-nio-8443-exec-12:116 - Retrieving attributes...
2017-04-24 12:27:35,445 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:182 - Getting authorities for user cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:27:35,445 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:182 - Getting authorities for user cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:27:35,448 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:211 - Searching for roles for user 'tuser@ihtd.com', DN = 'cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com', with filter (&(objectclass=group)(member={0})(cn=jasper_*)) in search base ''
2017-04-24 12:27:35,448 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:211 - Searching for roles for user 'tuser@ihtd.com', DN = 'cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com', with filter (&(objectclass=group)(member={0})(cn=jasper_*)) in search base ''
2017-04-24 12:27:35,449 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:150 - Using filter: (&(objectclass=group)(member=cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com)(cn=jasper_*))
2017-04-24 12:27:35,449 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-12:150 - Using filter: (&(objectclass=group)(member=cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com)(cn=jasper_*))
2017-04-24 12:27:35,885 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:219 - Roles from search: [Jasper_IM]
2017-04-24 12:27:35,885 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-12:219 - Roles from search: [Jasper_IM]
2017-04-24 12:27:35,887 DEBUG LdapUserDetailsMapper,http-nio-8443-exec-12:51 - Mapping user details from context with DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:27:35,887 DEBUG LdapUserDetailsMapper,http-nio-8443-exec-12:51 - Mapping user details from context with DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com

However, if the user is a member of another (security) group - which is a member of the mapped jasper role group the role search comes back empty

2017-04-24 12:30:50,852 DEBUG FilterBasedLdapUserSearch,http-nio-8443-exec-18:107 - Searching for user 'tuser@justassociates.com', with user search [ searchFilter: '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))', searchBase: '', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2017-04-24 12:30:50,852 DEBUG FilterBasedLdapUserSearch,http-nio-8443-exec-18:107 - Searching for user 'tuser@justassociates.com', with user search [ searchFilter: '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))', searchBase: '', scope: subtree, searchTimeLimit: 0, derefLinkFlag: false ]
2017-04-24 12:30:51,284 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:211 - Searching for entry under DN 'dc=ihazthadumb,dc=com', base = '', filter = '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))'
2017-04-24 12:30:51,284 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:211 - Searching for entry under DN 'dc=ihazthadumb,dc=com', base = '', filter = '(&(userPrincipalName={0})(&((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/ Role Groups,DC=ihazthadumb,DC=com))))'
2017-04-24 12:30:51,288 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:223 - Found DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced
2017-04-24 12:30:51,288 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:223 - Found DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced
2017-04-24 12:30:51,646 DEBUG BindAuthenticator,http-nio-8443-exec-18:108 - Attempting to bind as cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:30:51,646 DEBUG BindAuthenticator,http-nio-8443-exec-18:108 - Attempting to bind as cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:30:51,732 DEBUG BindAuthenticator,http-nio-8443-exec-18:116 - Retrieving attributes...
2017-04-24 12:30:51,732 DEBUG BindAuthenticator,http-nio-8443-exec-18:116 - Retrieving attributes...
2017-04-24 12:30:51,741 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:182 - Getting authorities for user cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:30:51,741 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:182 - Getting authorities for user cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:30:51,742 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:211 - Searching for roles for user 'tuser@justassociates.com', DN = 'cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com', with filter (&(objectclass=group)(member={0})(cn=jasper_*)) in search base ''
2017-04-24 12:30:51,742 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:211 - Searching for roles for user 'tuser@justassociates.com', DN = 'cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com', with filter (&(objectclass=group)(member={0})(cn=jasper_*)) in search base ''
2017-04-24 12:30:51,744 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:150 - Using filter: (&(objectclass=group)(member=cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com)(cn=jasper_*))
2017-04-24 12:30:51,744 DEBUG SpringSecurityLdapTemplate,http-nio-8443-exec-18:150 - Using filter: (&(objectclass=group)(member=cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com)(cn=jasper_*))
2017-04-24 12:30:52,152 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:219 - Roles from search: []
2017-04-24 12:30:52,152 DEBUG DefaultLdapAuthoritiesPopulator,http-nio-8443-exec-18:219 - Roles from search: []
2017-04-24 12:30:52,155 DEBUG LdapUserDetailsMapper,http-nio-8443-exec-18:51 - Mapping user details from context with DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com
2017-04-24 12:30:52,155 DEBUG LdapUserDetailsMapper,http-nio-8443-exec-18:51 - Mapping user details from context with DN: cn=Test User,ou=Test Accounts,ou=Service Accounts,ou=O365 Synced,dc=ihazthadumb,dc=com

Here is our AD layout:

Here's the LDAP config from my applicationContext-externalAuth-LDAP.xml (All other lines remain default)

<bean id="ldapAuthenticationProvider"
      class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSLdapAuthenticationProvider">
    <constructor-arg>
        <bean class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSBindAuthenticator">
            <constructor-arg>
                <ref local="ldapContextSource"/>
            </constructor-arg>
            <property name="userSearch" ref="userSearch"/>
        </bean>
    </constructor-arg>
    <constructor-arg>
        <bean class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSDefaultLdapAuthoritiesPopulator">
            <constructor-arg index="0">
                <ref local="ldapContextSource"/>
            </constructor-arg>
            <constructor-arg index="1">
                <value></value>
            </constructor-arg>
            <property name="groupRoleAttribute" value="cn"/>
            <property name="convertToUpperCase" value="true"/>
            <property name="rolePrefix"         value="ROLE_"/>
            <property name="groupSearchFilter"  value="(&amp;(objectclass=group)(member={0})(cn=jasper_*))"/>
            <property name="searchSubtree"      value="true"/> 
            <!-- Can setup additional external default roles here  <property name="defaultRole" value="LDAP"/> -->
        </bean>
    </constructor-arg>
</bean>
 
<bean id="userSearch" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSFilterBasedLdapUserSearch">
    <constructor-arg index="0">
        <value></value>
    </constructor-arg>
    <constructor-arg index="1">
      <value>(&amp;(userPrincipalName={0})(&amp;((objectclass=user)(memberOf:1.2.840.113556.1.4.1941:=CN=Jasper_Users,OU=Authorization/Role Groups,DC=ihazthadumb,DC=com))))</value>
    </constructor-arg>
    <constructor-arg index="2">
        <ref local="ldapContextSource" />
    </constructor-arg>
    <property name="searchSubtree">
        <value>true</value>
    </property>
</bean>
 
<bean id="ldapContextSource"
      class="com.jaspersoft.jasperserver.api.security.externalAuth.ldap.JSLdapContextSource">
    <constructor-arg value="ldaps://kittens.ihazthadumb.com:636/dc=ihazthadumb,dc=com"/>
    <!-- manager user name and password -->
    <property name="userDn"   value="CN=LDAP Authentication,OU=Application Service Accounts,DC=ihazthadumb,DC=com"/>
    <property name="password" value="This is a really long passphrase hopefully nobody would ever want to type out just to get access to AD/LDAP.  Is it more secure?  You decide."/>
    <property name="referral" value="follow" />
</bean>
 
 
<bean id="externalUserSetupProcessor" class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserSetupProcessor" parent="abstractExternalProcessor">
    <!--Default permitted role characters; others are removed. Change regular expression to allow other chars.
        <property name="permittedExternalRoleNameRegex" value="[A-Za-z0-9_]+"/>-->
 
    <property name="userAuthorityService">
        <ref bean="${bean.internalUserAuthorityService}"/>
    </property>
    <property name="defaultInternalRoles">
        <list>
            <value>ROLE_USER</value>
        </list>
    </property>
    <property name="organizationRoleMap">
        <map>
            <!-- Mapping Group Names Created as Jasper Roles to JRS Internal Roles -->
            <entry>
                <key>
                    <value>ROLE_JASPER_ADMINISTRATORS</value>
                </key>
                <value>ROLE_ADMINISTRATOR</value>
            </entry>
            <entry>
                <key>
                    <value>ROLE_JASPER_IM</value>
                </key>
                <value>ROLE_IM</value>
            </entry>
            <entry>
                <key>
                    <value>ROLE_JASPER_SUPERVISORS</value>
                </key>
                <value>ROLE_SUPERVISORS</value>
            </entry>
            <entry>
                <key>
                    <value>ROLE_JASPER_TRAINING</value>
                </key>
                <value>ROLE_TRAINING</value>
            </entry>
        </map>
    </property>
</bean>

tuser.ja's picture
Joined: Feb 3 2016 - 8:21am
Last seen: 1 year 6 months ago

You are a life saver Tuser! Your answer worked like a charm. Jaspersoft's AD integration is the most cumbersome and frustrating I've ever worked with. I'm just glad I found your post. Thanks again.

 

Cheers,

Dela.

dela.deyoungster - 2 years 1 month ago

This is really a very useful post! Thanks a ton.

dipali.vithalkar - 1 year 2 months ago

Thanks, it works finally!

triplem - 9 months 4 weeks ago

1 Answer:

1

After much trial and error, and some assumption on my part based on the existing UserSearch filter I have found the solution to my problem.  I hope this helps anyone wanting to leverage existing groups and user memberships, while still having a specific, manageable, configuration for including Jasper Reports into the organization's directory services for authentication and wuthorization.

Refernced documentation:
http://community.jaspersoft.com/documentation/jasperreports-server-authe...
http://community.jaspersoft.com/blog/jasperserver-user-authentication-mi...
http://community.jaspersoft.com/wiki/secure-active-directoryldapldaps-au...
http://forum.spring.io/forum/spring-projects/data/ldap/107516-find-all-s...
 

I changed the group search filter from:
<value>(&amp;(member={0})(objectclass=group)(cn=jasper_*))</value>
Which expects to find a user as a direct member of a group beginning with "jasper_" to match a role

to:
<property name="groupSearchFilter" value="(&amp;((objectClass=group)(member:1.2.840.113556.1.4.1941:={0})(cn=jasper_*)))"/>
Which, like the userSearch, recursively searches through the user's memberships for a group matching "jasper_" to match and map a role.

It is my understanding based on what I've read from the Spring documentation and other posts that the recursive functionality is not present in all LDAP implementations.  The referenced article https://msdn.microsoft.com/en-us/library/aa746475%28VS.85%29.aspx indicates that the rule OID above:

1.2.840.113556.1.4.1941 LDAP_MATCHING_RULE_IN_CHAIN This rule is limited to filters that apply to the DN. This is a special "extended" match operator that walks the chain of ancestry in objects all the way to the root until it finds a match.


 

tuser.ja's picture
Joined: Feb 3 2016 - 8:21am
Last seen: 1 year 6 months ago
Feedback
randomness