Search code examples
authenticationactive-directoryldapjasperserver

How to configuring jasper server with ldaps authentication?


I've been working on authenticating against an active directory server with jasper 6.4.0 for a while now, and have been getting the following error.

2017-10-16 13:39:35,145  WARN JSLdapAuthenticationProvider,http-apr-8080-exec-9:62 - [
LDAP: error code 49 - 80090308: LdapErr: DSID-0C09042F, comment: AcceptSecurityContext error, data 52e, v2580 ]; 
nested exception is javax.naming.AuthenticationException: 
[LDAP: error code 49 - 80090308: LdapErr: DSID-0C09042F, comment: AcceptSecurityContext error, data 52e, v2580 ]

From what I've gathered, data 52e indicates invalid credentials. I tried changing my service account password in my configuration to bob to see if I would get the same error (showing that it's an error when binding to the ldaps server rather than the test account I was logging in with), and I did.

Here is the configuration for the service account in jasper.

<bean id="ldapContextSource" class="com.jaspersoft.jasperserver.api.security.externalAuth.ldap.JSLdapContextSource">
    <constructor-arg value="ldaps://MyLDAPSServer:636/"/>
    <!-- manager user name and password (may not be needed)  -->
    <property name="userDn" value="dc=mydomain,dc=com,uid=MyServiceAccount"/>
    <property name="password" value="SomePasswordWithSpecialCharacters"/>
</bean>

I'm confident that the username and password are correct. I'm able to authenticate against the same ldaps server using the following Python code.

from ldap3 import Server, \
    Connection, \
    AUTO_BIND_NO_TLS, \
    SUBTREE, \
    ALL_ATTRIBUTES

def get_ldap_info(u):
    with Connection(Server('MyLDAPSServer', port=636, use_ssl=True),
                    auto_bind=AUTO_BIND_NO_TLS,
                    read_only=True,
                    check_names=True,
                    user='MyServiceAccount', password='SomePasswordWithSpecialCharacters') as c:

        c.search(search_base='DC=mydomain,DC=com',
                search_filter='(&(samAccountName=' + u + '))',
                search_scope=SUBTREE,
                attributes=ALL_ATTRIBUTES,
                get_operational_attributes=True)
        print(c.response_to_json())
        print(c.result)


get_ldap_info('test.user')

The one thing I've been able to think of, is maybe jasper doesn't like having special characters in the password?

Here is the remainder of the configuration for the jasper server in case I'm missing something.

<!--
 ~ Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 ~ http://www.jaspersoft.com.
 ~ Licensed under commercial Jaspersoft Subscription License Agreement
  -->

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

    <!-- ############ LDAP authentication ############
      - Sample configuration of external authentication via an external LDAP server.
    -->

    <bean id="proxyAuthenticationProcessingFilter" class="com.jaspersoft.jasperserver.api.security.EncryptionAuthenticationProcessingFilter"
          parent="mtAuthenticationProcessingFilter">
        <property name="authenticationManager">
            <ref local="ldapAuthenticationManager"/>
        </property>

        <property name="authenticationSuccessHandler" ref="externalAuthSuccessHandler" />
    </bean>

    <bean id="proxyAuthenticationSoapProcessingFilter"
          class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.MTDefaultAuthenticationSoapProcessingFilter">
        <property name="authenticationManager" ref="ldapAuthenticationManager"/>
        <property name="authenticationSuccessHandler" ref="externalAuthSuccessHandler" />

        <property name="filterProcessesUrl" value="/services"/>
    </bean>

    <bean id="proxyAuthenticationRestProcessingFilter" class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.MTDefaultAuthenticationRestProcessingFilter">
        <property name="authenticationManager">
            <ref local="ldapAuthenticationManager"/>
        </property>
        <property name="authenticationSuccessHandler" ref="externalAuthSuccessHandler" />
        <property name="filterProcessesUrl" value="/rest/login"/>
    </bean>

    <bean id="proxyRequestParameterAuthenticationFilter"
          class="com.jaspersoft.jasperserver.war.util.ExternalRequestParameterAuthenticationFilter" parent="requestParameterAuthenticationFilter">
        <property name="authenticationManager">
            <ref local="ldapAuthenticationManager"/>
        </property>
        <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
    </bean>

    <bean id="externalAuthSuccessHandler"
          class="com.jaspersoft.jasperserver.api.security.externalAuth.JrsExternalAuthenticationSuccessHandler" parent="successHandler">
        <property name="externalDataSynchronizer">
            <ref local="externalDataSynchronizer"/>
        </property>
    </bean>

    <bean id="proxyBasicProcessingFilter"
          class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.MTExternalAuthBasicProcessingFilter" parent="mtBasicProcessingFilter">
        <property name="authenticationManager" ref="ldapAuthenticationManager"/>
        <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
    </bean>

    <bean id="ldapAuthenticationManager" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.JSProviderManager">
        <property name="providers">
            <list>
                <ref local="ldapAuthenticationProvider"/>
                <ref bean="${bean.daoAuthenticationProvider}"/>
                <!--anonymousAuthenticationProvider only needed if filterInvocationInterceptor.alwaysReauthenticate is set to true
                <ref bean="anonymousAuthenticationProvider"/>-->
            </list>
        </property>
    </bean>

    <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="title"/>
                <property name="groupSearchFilter" value="(uid={1})"/>
                <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>cn=Users</value>
        </constructor-arg>
        <constructor-arg index="1">
            <!--<value>(uid={0})</value>-->
            <!--<value>(uid=test.user)</value>-->
            <value>(sAMAccountName={0})</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://MyLDAPSServer:636/"/>
    <!-- manager user name and password (may not be needed)  -->
    <property name="userDn" value="dc=mydomain,dc=com,uid=MyServiceAccount"/>
    <property name="password" value="SomePasswordWithSpecialCharacters"/>
</bean>
    <!-- ############ LDAP authentication ############ -->

    <!-- ############ JRS Synchronizer ############ -->
    <bean id="externalDataSynchronizer"
          class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.MTExternalDataSynchronizerImpl">
        <property name="externalUserProcessors">
            <list>
                <ref local="ldapExternalTenantProcessor"/>
                <ref local="mtExternalUserSetupProcessor"/>
                <!-- Example processor for creating user folder-->
                <!--<ref local="externalUserFolderProcessor"/>-->
            </list>
        </property>
    </bean>

    <bean id="abstractExternalProcessor" class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.AbstractExternalUserProcessor" abstract="true">
        <property name="repositoryService" ref="${bean.repositoryService}"/>
        <property name="userAuthorityService" ref="${bean.userAuthorityService}"/>
        <property name="tenantService" ref="${bean.tenantService}"/>
        <property name="profileAttributeService" ref="profileAttributeService"/>
        <property name="objectPermissionService" ref="objectPermissionService"/>
    </bean>

    <!--
        Multi-tenant configuration. For a JRS deployment with multiple
        organizations, modify this bean to set up your organizations. For
        single-organization deployments, comment this out and uncomment the version
        below.
    -->
    <bean id="ldapExternalTenantProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.processors.ldap.LdapExternalTenantProcessor" parent="abstractExternalProcessor">
        <property name="ldapContextSource" ref="ldapContextSource"/>
        <property name="multiTenancyService"><ref bean="internalMultiTenancyService"/></property>
        <property name="excludeRootDn" value="false"/>
        <!--only following LDAP attributes will be used in creation of organization hierarchy.
                Eg. cn=Smith,ou=Developement,o=Jaspersoft will produce tanant Development as child of
                tenant Jaspersoft (if excludeRootDn=false) as child of default tenant organization_1-->
        <property name="organizationRDNs">
            <list>
                <value>dc</value>
                <value>c</value>
                <value>o</value>
                <value>ou</value>
                <value>st</value>
            </list>
        </property>
        <property name="rootOrganizationId" value="organization_1"/>
        <property name="tenantIdNotSupportedSymbols" value="#{configurationBean.tenantIdNotSupportedSymbols}"/>

        <!-- User credentials are setup in js.externalAuth.properties-->
        <property name="externalTenantSetupUsers">
            <list>
                <bean class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.processors.MTAbstractExternalProcessor.ExternalTenantSetupUser">
                    <property name="username" value="${new.tenant.user.name.1}"/>
                    <property name="fullName" value="${new.tenant.user.fullname.1}"/>
                    <property name="password" value="${new.tenant.user.password.1}"/>
                    <property name="emailAddress" value="${new.tenant.user.email.1}"/>
                    <property name="roleSet">
                        <set>
                            <value>ROLE_ADMINISTRATOR</value>
                            <value>ROLE_USER</value>
                        </set>
                    </property>
                </bean>
            </list>
        </property>
    </bean>

    <!--
        Single tenant configuration. For a JRS deployment with a single
        organization, uncomment this bean and configure it to set up your organization.
        Comment out the multi-tenant version of ldapExternalTenantProcessor above
    -->
    <!--<bean id="ldapExternalTenantProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.processors.ldap.LdapExternalTenantProcessor" parent="abstractExternalProcessor">
        <property name="ldapContextSource" ref="ldapContextSource"/>
        <property name="multiTenancyService"><ref bean="internalMultiTenancyService"/></property>
        <property name="excludeRootDn" value="true"/>
        <property name="defaultOrganization" value="organization_1"/>
    </bean>-->

    <bean id="mtExternalUserSetupProcessor" class="com.jaspersoft.jasperserver.multipleTenancy.security.externalAuth.processors.MTExternalUserSetupProcessor" 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>
                <!-- Example of mapping customer roles to JRS roles -->
                <entry>
                    <key>
                        <value>ROLE_ADMIN_EXTERNAL_ORGANIZATION</value>
                    </key>
                    <!-- JRS role that the <key> external role is mapped to-->
                    <value>ROLE_ADMINISTRATOR</value>
                </entry>
            </map>
        </property>
    </bean>

    <!-- EXAMPLE Processor
    <bean id="externalUserFolderProcessor"
          class="com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserFolderProcessor"
          parent="abstractExternalProcessor">
        <property name="repositoryService" ref="${bean.unsecureRepositoryService}"/>
    </bean>
    -->
    <!-- ############ JRS Synchronizer ############ -->
</beans>

Solution

  • For anyone in the future who's struggling with this like I was, the problem was the userDn in my ldapContextSource.

    The correct userDn was

    <property name="userDn" value="CN=myserviceaccount,OU=my ou,DC=mydomain,DC=com"/>

    I found that by running this python script and looking at the output:

    from ldap3 import Server, \
        Connection, \
        AUTO_BIND_NO_TLS, \
        SUBTREE, \
        ALL_ATTRIBUTES
    
    def get_ldap_info(u):
        with Connection(Server('MyLDAPSServer', port=636, use_ssl=True),
                        auto_bind=AUTO_BIND_NO_TLS,
                        read_only=True,
                        check_names=True,
                        user='MyServiceAccount', password='SomePasswordWithSpecialCharacters') as c:
    
    
            c.search(search_base='DC=mydomain,DC=com',
                    search_filter='(&(samAccountName=' + u + '))',
                    search_scope=SUBTREE,
                    attributes=ALL_ATTRIBUTES,
                    get_operational_attributes=True)
            print(c.response_to_json())
            print(c.result)
    
    
    get_ldap_info('myserviceaccount')