Jump to content
We've recently updated our Privacy Statement, available here ×
  • Easily integrate TIBCO JasperReports® Server Community Edition with Kerberos+LDAP SSO in MS AD-environment


    peterf
    • Features: JasperReports Server, User Authorization Version: v8, v8.1, v8.1.0 Product: JasperReports® Server

    Introduction

    I needed a TIBCO JasperReports® Server running in a Linux VM integrated in the MS AD-environment, including a Kerberos-based single-sign-on and with name and email attached to the external users in JasperServer.

    With JasperReports Server 7.1.0 and it's builtin LDAP-support, some Wiki-pages, I thought this would be an easy task.

    Ok, it wasn't for me, but I hope, it will be for you, now!

    • As of 2018-08-23, this description is complete.
    • As of 2022-10-27 this description is tested with JasperReports Server 8.1.0
      • Search for 'Note 2022-10-27' and especially 'Note 2022-10-27 Changes from 7.1.0 to 8.1.0' to find the changes
    • 2022-11-11 Added a note about increasing the session-timeout when using Kerberos-based login

    Prerequisites

    • JasperReports® Server should run in it's own Linux VM

      • You will need the password of a domain administrator to enter the domain with this Linux VM!
    • Users are maintained in an ActiveDirectory-domain with Kerberos enabled

    • Domain-users at client PCs are Kerberos-enabled

      • This should be default for current Windows server and client systems
      • I haven't tried any Linux Samba server or client settings
    • You know how to install JasperReports Server, set up a Linux VM, Apache, etc.

      • You can use the system or bundled components of Tomcat, PostgreSQL, etc.
      • I decided to use the system Tomcat and PostgreSQL services and bundled PhantomJS
    • Browsers should be Kerberos-enabled for the domain to handle the ticket

      • E.g. for Firefox: Search for "Enabling Kerberos Authentication in Firefox" and you'll get the information how to set network.negotiate-auth.trusted-uris to your ".domain.com"
      • Firefox, Chrome, Edge, even IE should work
    • I used (K)Ubuntu for the Linux-VM, so you might have to adopt some commands, eg. if you use another package management system.

    • If you like to have the emailAddress and fullname of your LDAP-users mapped to the JasperReports Server external users, you have to compile and pack a Java class in a jar and deploy it to your JasperReports Server

    This has nothing to do with the waffle-based solution with a JasperReports Server running on a Windows-VM/server!


    Overview

    After struggling with my old (2011-2013) coded Kerberos/LDAP-implementation up to JasperReports Server 5.0, some Wiki-pages (thank you, if you wrote one!), Java policy-file etc. I decided to split Kerberos authentication, LDAP authorisation and JasperReports Server integration. This definitely eases the management and debugging of the authentication and authorization.

    I came up with the following solution:

    LevelUser/Status

    Apache (2.4)

    (automatic) Kerberos-based authentication resulting in user user
    • User authentication by Kerberos or BASIC
    Fallback to BASIC auth, if not in the domain
    • Append the username to the RequestHeader as X-Remote-User
    The next levels can access the authenticated user by the header field X-Remote-User
    • Connects locally via AJP to the Tomcat8
     
    • Optional restriction to valid-user or LDAP group
    Restrict the general access to the web-server

    Tomcat 8

    (either system or JasperReports Server included)

    Named authenticated user
    • Relies on the user authentication done by Apache
    No authentication by Tomcat!
    • Only locally accessible
    No side-injection of not-authenticated users

    JasperReports Server 8

    Named authenticated JS user with AD groups
    • Relies on the user authentication done by Apache
    username = X-Remote-User
    • Maps (all) LDAP the user belongs to to JasperReports Server roles

    roles = ROLE_LDAP_groups

    Custom UserSetupProcessor

    Named authenticated JS user with full name and email-address

    externalInetOrgPersonUserSetupProcessor using InetOrgPerson properties

     

    Uses LDAP to resolve name and mail-address

    fullname = LDAP displayName
    emailAddress = LDAP mail

     

    Advantages

    • (Automatic) Single-Sign-On for all domain users, fallback to BASIC authentication for none-domain access
      • Even JasperReports Server's great admin-feature "log in as user" works!
    • User and groups can be set up and maintained in AD
    • Permissions of reports can be assigned to certain AD users or (better!) to AD groups via their mapped roles
    • Mails can be sent to all users with a correct mail-address in their AD account
    • Manual login in JasperServer via the /jasperserver/login.html page with a local admin is still possible and you can use the "Login as user" for LDAP users.

      To login with Kerberos again, just logout and go back to /jasperserver, and you're in as Kerberos-user.


    Setup Kerberos-enabled Linux

    IP and hostname

    Get IP and FQ-hostname of your new jasperserver from your system administrator

    • Modify /etc/hosts with your values

      172.30.1.xxx      jasperserver.domain.name jasperserver

    Install necessary packages

    sudo apt install krb5-user samba sssd ntp

    Configure Kerberos and Samba

    See https://help.ubuntu.com/lts/serverguide/sssd-ad.html for details.

    Realm for Kerberos Version 5:

    DOMAIN.COM

    /etc/krb5.conf

    Add:

    [realms]
                    DOMAIN.COM = {
                                   kdc = domctrl.domain.com
                                   master_kdc = domctrl.domain.com
                                   admin_server = domctrl.domain.com
                              default_domain = DOMAIN
                    }
    
    [domain_realm]
        .domain.com = DOMAIN.COM
         domain.com = DOMAIN.COM

    /etc/ntp.conf

    # Specify one or more NTP servers.
    server domctrl.domain.com

    /etc/samba/smb.conf

    Note 2022-10-27:
    Don't set this option, if the hostname is equal to the netbios name.

    netbios name = jasperserver

    ### Active Directory
        workgroup = DOMAIN
        # netbios name = jasperserver
    
    #    client signing = yes
    #    client use spnego = yes
        kerberos method = secrets and keytab
        realm = DOMAIN.COM
        security = ads
        encrypt passwords = yes

    /etc/sssd/sssd.conf

    Note 2022-10-27:
    Don't set this option, if the FQ hostname is equal to the ad_hostname.

    ad_hostname = jasperserver.domain.com

    [sssd]
    services = nss, pam
    config_file_version = 2
    domains = DOMAIN.COM
    
    [domain/DOMAIN.COM]
    id_provider = ad
    access_provider = ad
    
    # Use this if users are being logged in at /.
    # This example specifies /home/DOMAIN-FQDN/user as $HOME.  Use with pam_mkhomedir.so
    override_homedir = /home/%d/%u
    
    # Uncomment if the client machine hostname doesn't match the computer object on the DC.
    # ad_hostname = jasperserver.domain.com

    Permissions /etc/sssd/sssd.conf

    sudo chown root:root /etc/sssd/sssd.conf
    sudo chmod 600 /etc/sssd/sssd.conf

    Optional:
    /etc/pam.d/common-session

    Append after session required pam_unix.so

    session required pam_mkhomedir.so skel=/etc/skel/ umask=0022

    /etc/hosts

    (see above)

    172.30.1.xxx    jasperserver.domain.com jasperserver

    If you don’t regard the sequence, with net ads join you might get
    DNS update failed: NT_STATUS_INVALID_PARAMETER

     

    Restart Services

    sudo systemctl restart ntp.service
    sudo systemctl restart smbd.service nmbd.service
    sudo systemctl start sssd.service
    

    Troubleshooting Samba

    Just in case…

    https://wiki.samba.org/index.php/Troubleshooting_Samba_Domain_Members

    Let the Linux-VM join the AD domain

    -------------------------------------------------------- 

    Restart Services
    (if not already done)

    sudo systemctl restart ntp.service
    sudo systemctl restart smbd.service nmbd.service
    sudo systemctl start sssd.service
    

    Log in as Domain-Admin

    sudo kinit Administrator
    

    Join the AD domain

    Note 2022-10-27:
    '-k' is deprecated, but still working

    sudo net ads join –k
    sudo net ads keytab add HTTP -k
    

    Check SPNs

    Note 2022-10-27:
    '-k' is deprecated, but still working

    sudo klist -e -k -t /etc/krb5.keytab
    

    Change permissions for Keytab file

    sudo chmod 640 /etc/krb5.keytab
    sudo chgrp www-data /etc/krb5.keytab
    

    Apache2-Restart

    (if it’s already configured)

    sudo systemctl restart apache2
    

     

    Log in to the Linux-machine as domain user

    Right now, you should be able to log into the Linux-VM as any domain user. This is a good test, if Kerberos is working, but it might not be the desired behavior in your case.

    Check out your Linux documentation on how to change it the way you need it.


    Setup Kerberos enabled Apache

    Install necessary packages

    sudo apt install apache2 libapache2-mod-auth-kerb
    

    Activate AuthLDAPURL and RequestHeader

    RequestHeader is needed e.g. for JasperServer

    sudo a2enmod authnz_ldap headers
    

    Activate Proxy modules

    You'll need it later to connect Apache

    sudo a2enmod proxy proxy_ajp
    

    Secure a Location with Kerberos plus BASIC-fallback

    /etc/apache2/sites-available/000-default.conf (or the one you use)

    Change the lines starting "->" to match your values.

    <Location / >
    
        AuthType Kerberos
        AuthName "Mit Windows-Konto einloggen! Login with Windows-Account!"
    
        # Browser Negotiate allowed
        KrbMethodNegotiate on
    
        # Fallback allowed (for Mobile- or other access (from another server, etc))
        KrbMethodK5Passwd on
        KrbSaveCredentials on
        
    ->    KrbAuthRealms DOMAIN.COM
        KrbServiceName HTTP
    
        Krb5Keytab /etc/krb5.keytab
    
    
        # Make sure that Kerberos passes the username only into Apache rather than the username@REALM
        # This is important if you’re doing additional authorization via LDAP as discussed below.
        KrbLocalUserMapping on
        
        # LDAP URL and Request-User-Credentials
    ->    AuthLDAPURL "ldap://domctrl.domain.com/dc=domain,dc=com?sAMAccountName"
        
        # LDAP-Query-User (Note: "AuthLDAPInitialBindAsUser on" can not be used as an alternative!)
    ->    AuthLDAPBindDN KerberosUser@domain.com
    ->    AuthLDAPBindPassword thePasswordOfTheKerberosUser
    
        # Search parameters
        AuthLDAPGroupAttribute member
        AuthLDAPGroupAttributeISDN On
        # AuthLDAPMaxSubGroupDepth 5
        AuthLDAPSubGroupClass group
    
         
    ->    # any authenticated user allowed (comment this out, if you use require ldap-group
    ->    require valid-user 
    
    ->    # You might (alternatively to require valid-user) want to restrict the access to certain LDAP groups
    ->    ## require ldap-group CN=GroupWithJasperAdmins,OU=subdepartment,OU=department,DC=domain,DC=com
    ->    ## require ldap-group CN=GroupWithJasperUsers,OU=subdepartment,OU=department,DC=domain,DC=com
    
        # Forward the username for "Auto-LDAPing"=Auto-Login with JasperServer 
        RequestHeader set X-Remote-User expr=%{REMOTE_USER}
    </Location>
    

    RequestHeader X-Remote-User (for JasperServer)

    The RequestHeader directive

    RequestHeader set X-Remote-User expr=%{REMOTE_USER}
    

    is needed for JasperServer only! Tomcat itself uses the credentials if tomcatAuthentication="false" is set as described below

    Restart Apache

    sudo systemctl restart apache2
    

    Needed every now and then...

    And open your servers URL http://jasperserver.domain.com to check, if the Apache is working fine.


    System Tomcat or JasperServer Tomcat?

    You can alternatively use the system Tomcat or the Tomcat included in the JasperReports Server installation. The latter is my preference for development and debugging, because you don't have to struggle with Java-versions or other problems unrelated to your primary task.

    Official guide says: The Binary Installer is intended for evaluation purposes only. To install TIBCO JasperReports Server for enterprise production environments, use the stand-alone WAR file distribution, which is the official TIBCO JasperReports Server installer. This implies that using your own (system) Tomcat is strongly recommended for Production environments.

    You should install JasperServer according to your choice using the internal or system components.

    If you like to install both editions, don't forget to switch off the one you do not need and enable the other one.

    Installation paths for external components

    For JasperServer installation with Ubuntu 18.4

    PhantomJS

    /usr/bin/phantomjs

    PostgreSQL

    /usr/lib/postgresql/10/bin

    Tomcat 8

    /var/lib/tomcat8

    Note 2022-10-27:

    I haven't checked these with Ubuntu 22.04

    Switch system Tomcat on or off

    (Re-)start system Tomcat

    sudo systemctl restart tomcat8.service
    

    Stop system Tomcat

    sudo systemctl stop tomcat8.service
    

    Switch JasperServer-included Tomcat on or off

    (Re-)start JasperServer-included Tomcat:

    ./ctlscript.sh restart tomcat
    

    Stop JasperReports Server included Tomcat:

    ./ctlscript.sh stop tomcat
    

    Install system Tomcat

    Install necessary packages

    sudo apt install tomcat8
    

    Optional stuff:

    sudo apt install tomcat8-admin tomcat8-docs tomcat8-examples
    
    • Includes:
      • Admin-GUI tomcat8-admin
      • Documentation tomcat8-docs
      • Examples tomcat8-examples

    Ease your life by entering the group tomcat8

    sudo usermod -aG tomcat8 <myusername>
    

    Do this for PostgreSQL, too, if you like

    sudo usermod -aG postgres <myusername>
    

    When using internal Tomcat installed with sudo to /opt

    Make the Tomcat 8 conf-directory accessible for users of the Tomcat8 group:

    sudo chmod g+x /opt/jasperreports-server-cp-7.1.0/apache-tomcat/conf
    

    And re-login to your Linux!


    Setup Tomcat using Apache authenticated username

    Tomcat's own authentication must be disabled:

    tomcatAuthentication="false"
    

    External access from other hosts than localhost must be prevented:

    address="127.0.0.1"
    

    Change conf/server.xml in <Service name="Catalina">

    <Connector port="8080" URIEncoding="UTF-8" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" address="127.0.0.1" />
    <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" tomcatAuthentication="false" address="127.0.0.1"/>
    

    (see https://stackoverflow.com/questions/13749065/forward-remote-user-to-tomcat-via-ajp-e-g-for-shibboleth)

    If you use JasperServer Studio, please see the Known Issues.

    Debug: Change the Tomcat index.jsp-file to show the username

    The JasperReports Server includes a Tomcat ROOT webapp which provides a nice index.jsp page. The Ubuntu packages only installs a simple index.html.

    Use index.jsp-ROOT instead of index.html-ROOT

    • Rename /var/lib/tomcat8/webapps/ROOT

      sudo mv /var/lib/tomcat8/webapps/ROOT /var/lib/tomcat8/webapps/ROOT_from_package
      
    • Copy JasperReports Servers webapps/ROOT to /var/lib/tomcat8/webapps/ROOT and fix permissions

      sudo cp -r <jasperreports-server>/apache-tomcat/webapps/ROOT /var/lib/tomcat8/webapps/ROOT
      sudo chown -R root:tomcat8 /var/lib/tomcat8/webapps/ROOT
      

    Show the username of the authenticated user

    In the webapps/ROOT/index.jsp find the line (it was line 52 for me)

    <h2>If you're seeing this, you've successfully installed Tomcat. Congratulations!</h2>
    

    and replace it with (something like)

    <h2>Hello user '${pageContext.request.remoteUser}', if you're seeing this, you've successfully installed Tomcat. Congratulations!</h2>
    <p>User remote: <%=request.getRemoteUser()%></p>
    <p>User principal: <%=request.getUserPrincipal()%></p>
    

    Connect Apache with Tomcat via ProxyPass and forward username to JasperReports Server

    For the Tomcat connection you have to change the location (if you need), the ProxyPass-settings and the RequestHeader directive.

    In your productive system, you only might want to forward the location /jasperserver, the ProxyPass is as follows

    # Important: Deactivate Tomcat´s own authentication by AJP and restrict access to localhost in server.xml: tomcatAuthentication="false" address="127.0.0.1"
    #     <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" tomcatAuthentication="false" address="127.0.0.1" />
    
    ProxyPass ajp://localhost:8009/jasperserver
    ProxyPassReverse ajp://localhost:8009/jasperserver
    
    # Forward username as RequestHeader
    RequestHeader set X-Remote-User expr=%{REMOTE_USER}
    

    Final Tomcat-connection configuration in Apache

    # Integration Tomcat
    <IfModule mod_proxy_ajp.c>
        <Location /jasperserver>
            # Important: Deactivate Tomcat´s own authentication by AJP and restrict access to localhost in server.xml: tomcatAuthentication="false" address="127.0.0.1"
            # <Connector port="8009" protocol="AJP/1.3" redirectPort="8443" tomcatAuthentication="false" address="127.0.0.1" />
    
            ProxyPass ajp://localhost:8009/jasperserver
            ProxyPassReverse ajp://localhost:8009/jasperserver
            
            AuthType Kerberos
            AuthName "JasperServer: Mit Windows-Konto einloggen! Login with Windows-Account!"
        
            # Browser Negotiate allowed
            KrbMethodNegotiate on
        
            # Fallback allowed (for Mobile- or other access (from another server, etc))
            KrbMethodK5Passwd on
            KrbSaveCredentials on
        
        ->    KrbAuthRealms DOMAIN.COM
            KrbServiceName HTTP
        
            Krb5Keytab /etc/krb5.keytab
        
        
            # Make sure that Kerberos passes the username only into Apache rather than the username@REALM
            # This is important if you’re doing additional authorization via LDAP as discussed below.
            KrbLocalUserMapping on
        
            # LDAP URL and Request-User-Credentials
        ->    AuthLDAPURL "ldap://domctrl.domain.com/dc=domain,dc=com?sAMAccountName"
        
            # LDAP-Query-User (Note: "AuthLDAPInitialBindAsUser on" can not be used as an alternative!)
        ->    AuthLDAPBindDN KerberosUser@domain.com
        ->    AuthLDAPBindPassword thePasswordOfTheKerberosUser
        
            # Search parameters
            AuthLDAPGroupAttribute member
            AuthLDAPGroupAttributeISDN On
            # AuthLDAPMaxSubGroupDepth 5
            AuthLDAPSubGroupClass group
    
        ->    # any authenticated user allowed (comment this out, if you use require ldap-group
        ->    require valid-user
        
        ->    # You might (alternatively to require valid-user) want to restrict the access to certain LDAP groups
        ->    ## require ldap-group CN=GroupWithJasperAdmins,OU=subdepartment,OU=department,DC=domain,DC=com
        ->    ## require ldap-group CN=GroupWithJasperUsers,OU=subdepartment,OU=department,DC=domain,DC=com
    
            # Forward the username for "Auto-LDAPing"=Auto-Login with JasperServer 
            RequestHeader set X-Remote-User expr=%{REMOTE_USER}     
       </Location>
    </IfModule>
    

    Test it with a redirect

    For testing purposes, you probably prefer a redirect of the root location / as follows

    <IfModule mod_proxy_ajp.c>
       <Location / >
           ProxyPass ajp://localhost:8009/
           ProxyPassReverse ajp://localhost:8009/
    [...]
    

    Open http://jasperserver.domain.com to access your Tomcat index.jsp

    This worked once, but not anymore (don't know why):

    If you open http://jasperserver.domain.com (with or without trailing slash), you'll see the Apache frontpage. Just append a question mark to get the Tomcat index.jsp page: http://jasperserver.domain.com/? (again: The slash doesn't matter: With http://jasperserver.domain.com? you will be redirected)

    Are you authenticated?

    If everything with configured correctly (and you have restarted Apache and Tomcat), at http://jasperserver.domain.com/? you should see something like:

    Hello user 'username', if you're seeing this, you've successfully installed Tomcat. Congratulations!

    User remote: username

    User principal: CoyotePrincipal[username]

    Great! How about a snapshot of the VM right now?

    • To be sure: Just comment out require valid-user in your /etc/apache2/sites-available/000-default.conf, restart your Apache sudo systemctl restart apache2 and try again: Ups, internal server error. Change it back, restart Apache and it's working again!
    • If it's not working: Did you allow your browser to negotiate the tickets?

    Install JasperReports Server with LDAP-support

    You might have an installed installation of JasperReports Server. Then just adopt it to the Tomcat installed above.

    Enable LDAP

    In either case, you must add the LDAP settings to your buildomatic/default_master.properties below part 9. The values are the same you already used in the Apache-configuration, without the parameter "?sAMAccountName" in the URL.

    # 9) External Authentication Data Sources
    # ...
    external.ldapUrl=ldap://domctrl.domain.com/dc=domain,dc=com
    external.ldapDn=KerberosUser@domain.com
    external.ldapPassword=thePasswordOfTheKerberosUser
    

    Then (re-)install this with

    cd buildomatic
    sudo ./js-install-ce.sh minimal
    

    or

    cd buildomatic
    sudo ./js-install-ce.sh regen-config
    

    Take care: Calling js-install-ce.sh without a parameter will install the demo databases!

    Check, if you can access the JasperServer, get to the login-page and be able to log in. (See the JasperReports Server installation guide for details)

    Change webapp configuration if not applied

    Sometimes, these settings are not applied by using buildomatic.

    You might append or change WEB-INF/js.config.properties with the same lines used with '9) External Authentication Data Sources'.


    Enable Pre-Authentication with LDAP-username

    Now you need the following /var/lib/tomcat8/webapps/jasperserver/WEB-INF/applicationContext-externalAuth-LDAP.xml

    Save the file and fix the permissions with

    sudo chown -R root:tomcat8 /var/lib/tomcat8/webapps/jasperserver/WEB-INF/applicationContext-externalAuth-LDAP.xml
    

    /jasperserver/WEB-INF/applicationContext-externalAuth-LDAP.xml

    I tried to write some useful comments in this file, so it's hopefully self-explanatory.

    Note 2022-10-27 Changes from 7.1.0 to 8.1.0

    7.1.0

    8.1.0

    <ref local=<ref bean=
    Replace all occurrences
    <beans ... xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">
    <beans ... http://www.springframework.org/schema/beans/spring-beans-4.0.xsd">
        <bean id="proxyBasicProcessingFilter" class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalAuthBasicProcessingFilter" parent="basicProcessingFilter">
            <property name="authenticationManager" ref="ldapAuthenticationManager"/>
     
        <bean id="proxyBasicProcessingFilter" class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalAuthBasicProcessingFilter" parent="basicProcessingFilter">
            <constructor-arg index="0"><ref bean="ldapAuthenticationManager"/></constructor-arg>
    <!-- not working from sample file:        <property name="authenticationManager" ref="ldapAuthenticationManager"/> -->
        <bean id="ldapAuthenticationManager" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.JSProviderManager">
            <property name="providers">
                <list>
                    <ref local="preAuthProvider"/>
                    <ref bean="${bean.daoAuthenticationProvider}"/>
                </list>
            </property>
        <bean id="ldapAuthenticationManager" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.JSProviderManager">
            <constructor-arg index="0">
                <list>
                    <ref bean="preAuthProvider"/>
                    <ref bean="${bean.daoAuthenticationProvider}"/>
                </list>
            </constructor-arg>
    <bean id="externalUserSetupProcessor" ...> ... </bean>
     
    Remove the bean
    Or get it from the sample file and fix it, if you'd like to try the test below
    <!--
      ~ Copyright (C) 2005 - 2022 TIBCO Software Inc. All rights reserved.
      ~ http://www.jaspersoft.com.
      ~
      ~ Unless you have purchased a commercial license agreement from Jaspersoft,
      ~ the following license terms apply:
      ~
      ~ This program is free software: you can redistribute it and/or modify
      ~ it under the terms of the GNU Affero General Public License as
      ~ published by the Free Software Foundation, either version 3 of the
      ~ License, or (at your option) any later version.
      ~
      ~ This program is distributed in the hope that it will be useful,
      ~ but WITHOUT ANY WARRANTY; without even the implied warranty of
      ~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
      ~ GNU Affero General Public License for more details.
      ~
      ~ You should have received a copy of the GNU Affero General Public License
      ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
      -->
    
    <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-4.0.xsd">
    
        <!-- ############ PreAuthentication with LDAP-based userdetails ############
          - Configuration of pre-authentication via an external LDAP server.
        -->
    
        <!-- ############ PreAuth ############ -->
        <!--https://community.jaspersoft.com/wiki/configuring-ldap-using-http-headers-jasperreports-server-60-sso-framework-->
    
        <!-- This pre-authentication header will handle LDAP authentication using a request header -->
        <bean id="preAuthProvider" class="org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider">
            <property name="preAuthenticatedUserDetailsService">
                <ref bean="wrappedUserDetailsService"/>
            </property>
            <property name="throwExceptionWhenTokenRejected" value="false"/>
        </bean>
    
        <!-- This wrapped user details service is used by the preauth provider defined above and provides a hook into the LdapUserDetails Service -->
        <bean id="wrappedUserDetailsService" class="org.springframework.security.core.userdetails.UserDetailsByNameServiceWrapper">
            <property name="userDetailsService">
                <ref bean="ldapUserDetailsService"/>
            </property>
        </bean>
    
        <!-- This LdapUserDetailsService creates UserDetails objects using the userSearch and ldapAuthoritiesPopulator beans
            and creates a User-object according to the userDetailsMapper -->
        <bean id="ldapUserDetailsService" class="org.springframework.security.ldap.userdetails.LdapUserDetailsService">
            <constructor-arg index="0">
                <ref bean="userSearch" />
            </constructor-arg>
            <constructor-arg index="1">
                <ref bean="ldapAuthPopulator" />
            </constructor-arg>
            <property name="userDetailsMapper" ref="userDetailsMapper"/>
        </bean>
    
    
        <!-- InetOrgPersonContextMapper is needed for the emailAddress -->
        <bean id="userDetailsMapper" class="org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper" />
    <!--     <bean id="userDetailsMapper" class="org.springframework.security.ldap.userdetails.LdapUserDetailsMapper" /> -->
    
        <!-- This filter will be added to the wildcard filter chain to intercept the request headers and pass them to the authentication manager -->
        <bean id="proxyPreAuthenticatedProcessingFilter" class="com.jaspersoft.jasperserver.api.security.externalAuth.preauth.BasePreAuthenticatedProcessingFilter">
            <!--
            The name of the principalRequestHeader field must fit the name set in the Apache configuration:
                RequestHeader set X-Remote-User expr=%{REMOTE_USER}
            -->
            <property name="principalParameter" value="X-Remote-User"/>
            <property name="tokenDecryptor" ref="plainTextTokenDecryptor"/>
            <!-- Configuration determining whether principalParameter is read from header (default) or from request parameter. -->
            <property name="tokenInRequestParam" value="false"/>
    
            <property name="authenticationManager" ref="ldapAuthenticationManager"/>
            <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
        </bean>
    
        <!-- Username is unencrypted -->
        <bean id="plainTextTokenDecryptor" class="com.jaspersoft.jasperserver.api.common.crypto.DevelopmentPlainTextNonCipher"/>
    
        <!--
            Must be disabled.
            Refer to
                https://community.jaspersoft.com/wiki/jasperreports-server-601-authentication-ntlm
                Section 3. "Comment out the proxyAuthenticationProcessingFilter bean:"
    
            <bean id="proxyAuthenticationProcessingFilter" class="com.jaspersoft.jasperserver.api.security.EncryptionAuthenticationProcessingFilter" />
    
        -->
    
    
        <!-- ############ PreAuth ############ -->
    
    
        <bean id="proxyRequestParameterAuthenticationFilter" class="com.jaspersoft.jasperserver.war.util.ExternalRequestParameterAuthenticationFilter" parent="requestParameterAuthenticationFilter">
            <property name="authenticationManager" ref="ldapAuthenticationManager"/>
            <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
        </bean>
    
        <bean id="externalAuthSuccessHandler" class="com.jaspersoft.jasperserver.api.security.externalAuth.JrsExternalAuthenticationSuccessHandler" parent="successHandler">
            <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
        </bean>
    
        <bean id="proxyBasicProcessingFilter" class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalAuthBasicProcessingFilter" parent="basicProcessingFilter">
            <constructor-arg index="0">
                <ref bean="ldapAuthenticationManager"/>
            </constructor-arg>
    <!-- not working from sample file:        <property name="authenticationManager" ref="ldapAuthenticationManager"/> -->
            <property name="externalDataSynchronizer" ref="externalDataSynchronizer"/>
        </bean>
    
        <bean id="ldapAuthenticationManager" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.JSProviderManager">
            <constructor-arg index="0">
                <list>
                    <ref bean="preAuthProvider"/>
    <!--                 <ref bean="ldapAuthenticationProvider"/> -->
                    <ref bean="${bean.daoAuthenticationProvider}"/>
    
                    <!--anonymousAuthenticationProvider only needed if filterInvocationInterceptor.alwaysReauthenticate is set to true
                    <ref bean="anonymousAuthenticationProvider"/>-->
                </list>
            </constructor-arg>
        </bean>
    
    <!--    <bean id="anonymousAuthenticationProvider" class="org.springframework.security.providers.anonymous.AnonymousAuthenticationProvider">
            <property name="key"><value>foobar</value></property>
        </bean>-->
    
    
        <!--
            LDAP-based populator with a groupSearchFilter specific for Microsoft-AD:
            It returns all groups a user is member of, including cascaded groups
            Explanation (in German): https://www.faq-o-matic.net/2017/09/06/verschachtelte-ad-gruppen-mit-ldap-filter-abfragen-2/
            English example: https://community.jaspersoft.com/questions/1048501/ldap-ad-2012-nested-ad-groups-and-role-mapping
    
            This must be adopted for other LDAP-servers!
    
            See Auth Cookbook for details (and problems)
                https://community.jaspersoft.com/documentation/jasperreports-server-authentication-cookbook/v550/overview-external-ldap
    
                e.g.: The beans that perform LDAP authentication do not map information such as the user’s full name, email address, or profile attributes that may exist in the LDAP directory. This requires customizing the FilterBasedLdapUserSearch bean, as described in Advanced Topics.
        -->
        <bean id="ldapAuthPopulator" class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSDefaultLdapAuthoritiesPopulator">
            <constructor-arg index="0">
                <ref bean="ldapContextSource"/>
            </constructor-arg>
            <constructor-arg index="1">
                <value></value>
            </constructor-arg>
            <property name="groupRoleAttribute" value="cn"/>
            <!-- Get all (cascaded) groups -->
            <property name="groupSearchFilter" value="(&amp;(objectCategory=group)(member:1.2.840.113556.1.4.1941:={0}))"/>
            <property name="searchSubtree" value="true"/>
            <!-- Seems to be an MS-AD specific -->
            <property name="ignorePartialResultException" value="true"/>
    
            <!--
                rolePrefix is deprecated. (at least with JS 7.1.0): Map the authorities in the {@code AuthenticationProvider} using a {@code GrantedAuthoritiesMapper}.
                    Problem of this deprecation: There a JS wrapper class {@code org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider} for the spring class {@code org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationProvider}, but it doesn't offer support for {@code GrantedAuthoritiesMapper}
                    So I left it as it is (and it works right now (JS 7.1.0))
            -->
            <property name="rolePrefix">
                <value>ROLE_LDAP_</value>
            </property>
            <!--
                convertToUpperCase is deprecated. (at least with JS 7.1.0): Convert case in the {@code AuthenticationProvider} using a {@code GrantedAuthoritiesMapper}.
            -->
            <property name="convertToUpperCase" value="false"/>
            <!--
                Setting the defaultRole is deprecated. (at least with JS 7.1.0): Assign a default role in the {@code AuthenticationProvider} using a {@code GrantedAuthoritiesMapper}.
                Just DON'T use it! (With ExternalInetOrgPersonUserSetupProcessor, this role was detected as externally defined, renamed to *_EXT and effectively broken)
            -->
            <!--         <property name="defaultRole" value="ROLE_USER"/> -->
        </bean>
    
    
        <!--
            Microsoft-AD specific search by sAMAccountName
            (similar to https://community.jaspersoft.com/documentation/tibco-jasperreports-server-authentication-cookbook/v601/authentication-microsoft )
        -->
        <bean id="userSearch" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
    <!--    <bean id="userSearch"
               class="de.stueken.security.externalAuth.wrappers.spring.ldap.AttributedFilterBasedLdapUserSearch"> -->
    
    <!--           class="com.jaspersoft.jasperserver.api.security.externalAuth.wrappers.spring.ldap.JSFilterBasedLdapUserSearch"> -->
            <constructor-arg index="0">
                <value></value>
            </constructor-arg>
            <constructor-arg index="1">
                <value>(sAMAccountName={0})</value>
            </constructor-arg>
            <constructor-arg index="2">
                <ref bean="ldapContextSource" />
            </constructor-arg>
            <property name="searchSubtree">
                <value>true</value>
            </property>
            <property name="returningAttributes">
                <list>
                    <!-- You need at least cn and sn.
                        Without cn, the mapping gives a NullPointerException.
                        Without sn you'll get an IllegalArgumentException: [Assertion failed] in org.springframework.security.ldap.userdetails.Person -->
                    <value>cn</value>
                    <value>sn</value>
    
                    <!-- displayName and mail is the stuff you are here for -->
                    <!-- Used for JS fullname -->
                    <value>displayName</value>
                    <!-- Used for JS emailAddress -->
                    <value>mail</value>
    
                    <!-- Now for the optional rest ...-->
                    <value>sAMAccountName</value>
    <!--                 <value>name</value> -->
                </list>
            </property>
    
        </bean>
    
        <!--
            Use settings taken form js.externalAuth.properties
            userDN (like "Technical.User@domain.tld") and password are necessary, if the LDAP doesn't allow anonymous requests.
        -->
        <bean id="ldapContextSource" class="com.jaspersoft.jasperserver.api.security.externalAuth.ldap.JSLdapContextSource">
            <constructor-arg value="${external.ldap.url}"/>
            <!-- manager user name and password (may not be needed)  -->
            <property name="userDn" value="${external.ldap.username}"/>
            <property name="password" value="${external.ldap.password}"/>
            <property name="referral" value="follow"/>
        </bean>
        <!-- ############ LDAP authentication ############ -->
    
        <!-- ############ JRS Synchronizer ############ -->
        <bean id="externalDataSynchronizer" class="com.jaspersoft.jasperserver.api.security.externalAuth.ExternalDataSynchronizerImpl">
            <property name="externalUserProcessors">
                <list>
                    <!-- Choose externalInetOrgPersonUserSetupProcessor or externalUserSetupProcessor -->
                    <!-- applies displayName as fullname and mail as emailAddress to the jasperserver user, but needs the class ExternalInetOrgPersonUserSetupProcessor -->
                    <ref bean="externalInetOrgPersonUserSetupProcessor"/>
    
                    <!-- doesn't apply the InetOrgPerson-properties to the jasperserver user, but doesn't need the extra classes -->
    <!--                 <ref bean="externalUserSetupProcessor"/>  -->
    
                    <!-- Example processor for creating user folder-->
                    <!--<ref bean="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>
    
        <!-- The ExternalInetOrgPersonUserSetupProcessor applies displayName as fullname and mail as emailAddress to the jasperserver user -->
        <bean id="externalInetOrgPersonUserSetupProcessor" class="de.stueken.jasperserver.api.security.externalAuth.processors.ExternalInetOrgPersonUserSetupProcessor" 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>
            <!-- These are the default attribute names 'displayName' and 'mail'
                Be careful on changes: Value must be an InetOrgPerson String property, it is case-sensitive, and must fit the returningAttributes of the userSearch
    
                <property name="fullNameAttribute" value="displayName"/>
                <property name="emailAddressAttribute" value="mail"/>
            -->
    
            <property name="adminUsernames">
                <!--Add yourself or another admins username, if you like. The following defaultAdminRoles must be set!-->
                <list>
                        <!--<value>Username</value>-->
                    </list>
            </property>
            <property name="defaultAdminRoles">
                <list>
                    <!-- Don't use ldapAuthPopulator's property defaultRole with the same role! -->
                    <value>ROLE_ADMINISTRATOR</value>
                </list>
            </property>
            <property name="defaultInternalRoles">
                <list>
                    <!-- Don't use ldapAuthPopulator's property defaultRole with the same role! -->
                    <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 ############ -->
    
        <!-- ############ Useless RequestHeaderAuthenticationFilter ############ -->
        <!--
            Form e.g. https://community.jaspersoft.com/questions/851215/container-security-community-server-601
                RequestHeaderAuthenticationFilter is useless, if you need the externalDataSynchronizer, because it's
                inherited from org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter
    
                See comment in com.jaspersoft.jasperserver.api.security.externalAuth.preauth.BasePreAuthenticatedProcessingFilter
                    * This filter class extends AbstractPreAuthenticatedProcessingFilter to
                    * call externalDataSynchronizer on successful authentication.
        -->
        <!--
            This filter will be added to the wildcard filter chain to intercept the request headers and pass them to the authentication manager.
            This filter must not throw an exception to allow other filters to show a login screen
            The name of the principalRequestHeader field must fit the name set in the Apache configuration:
                RequestHeader set X-Remote-User expr=%{REMOTE_USER}
        -->
    <!--    <bean id="proxyPreAuthenticatedProcessingFilter_SPRING" class="org.springframework.security.web.authentication.preauth.RequestHeaderAuthenticationFilter">
            <property name="principalRequestHeader" value="X-Remote-User"/>
            <property name="exceptionIfHeaderMissing" value="false"/>
            <property name="authenticationManager" ref="ldapAuthenticationManager"/>
        </bean>-->
        <!-- ############ Useless RequestHeaderAuthenticationFilter ############ -->
    
    
    </beans>

    Change for debugging tests without fullname and email

    If you like to test it first without the mapping of emailAddress and fullname, just go to the externalDataSynchronizer bean and enable externalUserSetupProcessor instead of externalInetOrgPersonUserSetupProcessor, like:

                    <!-- applies displayName as fullname and mail as emailAddress to the JasperReports Server user, but needs the class ExternalInetOrgPersonUserSetupProcessor -->
                    <!-- <ref local="externalInetOrgPersonUserSetupProcessor"/> -->
    
                    <!-- doesn't apply the InetOrgPerson-properties to the JasperReports Server user, but doesn't need the extra classes -->
                    <ref local="externalUserSetupProcessor"/> 
    

    You must comment out the bean definition of ExternalInetOrgPersonUserSetupProcessor, if it's not available in the classpath!

    Enable at least INFO-logging of com.jaspersoft.jasperserver.api.security in WEB-INF/log4j.properties:

    # Security Logging
    log4j.logger.com.jaspersoft.jasperserver.api.security=INFO
    

    Map displayName to fullname and mail to emailAddress

    Therefore, you need to compile the following Java-class, put it in a jar (mine is called jasperserverX-externalUserSetup.1.0.jar) and place it in the jasperserver/WEB-INF/lib

    You might want to throw out the reflection stuff and make it more hard-coded, if you like.

    /**
     *
     */
    package de.stueken.jasperserver.api.security.externalAuth.processors;
    
    import java.lang.reflect.Field;
    
    /** commons-logging-1.1.1.jar */
    import org.apache.commons.logging.Log;
    import org.apache.commons.logging.LogFactory;
    /** spring-security-core-3.2.10.RELEASE.jar */
    import org.springframework.security.core.Authentication;
    import org.springframework.security.core.context.SecurityContextHolder;
    /** spring-security-ldap-3.2.10.RELEASE-sources.jar */
    import org.springframework.security.ldap.userdetails.InetOrgPerson;
    import org.springframework.util.Assert;
    
    /** jasperserver-api-metadata-7.1.0.jar
     * jasperserver-api-metadata-impl-7.1.0.jar
     */
    import com.jaspersoft.jasperserver.api.metadata.user.domain.User;
    /** jasperserver-api-externalAuth-7.1.0.jar
     * jasperserver-api-externalAuth-impl-7.1.0.jar
    */
    import com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserSetupProcessor;
    
    /**
     * An {@link InetOrgPerson} has a {@link InetOrgPerson#getDisplayName()} as the full name and a
     * {@link InetOrgPerson#getMail()} as email address. This processor grabs both properties from the
     * {@link Authentication} object and applies them to the {@link User}.
     *
     * @version 1.0
     * @author Peter F. / stueken.de
     * @copyright 2018
     *
     */
    
    public class ExternalInetOrgPersonUserSetupProcessor extends ExternalUserSetupProcessor
    {
    
        private static final Log logger = LogFactory
                .getLog(ExternalInetOrgPersonUserSetupProcessor.class);
    
        private String fullNameAttribute = "displayName"; //$NON-NLS-1$
        private String emailAddressAttribute = "mail"; //$NON-NLS-1$
    
        /**
         * Updates the {@link User} user with full name and email address from the {@link InetOrgPerson}
         * principal. If no display name is provided for the principal, the username is used as full name.
         *
         * @see com.jaspersoft.jasperserver.api.security.externalAuth.processors.ExternalUserSetupProcessor#
         *      updateUserAttributes(com.jaspersoft.jasperserver.api.metadata.user.domain.User)
         */
        @SuppressWarnings("nls")
        @Override
        protected void updateUserAttributes(User user)
        {
            Assert.notNull(user, "User must be provided!");
    
            super.updateUserAttributes(user);
    
            // Get principal as InetOrgPerson
            InetOrgPerson person = getInetOrgPerson();
    
            if (person != null)
            {
                // Fallback to Username
                /*
                 * 'Hardcoded' version: String fullName = person.getDisplayName();
                 */
                String fullName = getString(person, getFullNameAttribute());
                if (fullName == null)
                    fullName = person.getUsername();
                if ((user.getFullName() == null) || !user.getFullName().equalsIgnoreCase(fullName))
                {
                    if (logger.isInfoEnabled())
                        logger.info("User '" + user.getUsername() + "' got new fullname: '" + fullName
                                + "', was '" + user.getFullName() + "'");
                    user.setFullName(fullName);
                }
    
                // get email
                /*
                 * 'Hardcoded' version: String emailAddress = person.getMail();
                 */
                String emailAddress = getString(person, getEmailAddressAttribute());
                if ((user.getEmailAddress() == null)
                        || !user.getEmailAddress().equalsIgnoreCase(emailAddress))
                {
                    if (logger.isInfoEnabled())
                        logger.info("User '" + user.getUsername() + "' got new emailAddress: '" + emailAddress
                                + "', was '" + user.getEmailAddress() + "'");
                    user.setEmailAddress(emailAddress);
                }
    
            }
    
            if (logger.isInfoEnabled())
                logger.info("Check UserAttributes for '" + user.getUsername() + "': " + user.getFullName()
                        + " <" + user.getEmailAddress() + ">");
    
        }
    
        /**
         *
         * @return principal as InetOrgPerson or <code>null</code>, if no Authentication or no Principal
         *         (as InetOrgPerson)
         */
        protected InetOrgPerson getInetOrgPerson()
        {
            Authentication auth = SecurityContextHolder.getContext().getAuthentication();
    
            if (auth != null)
            {
                logger.debug("Authentication of " + auth.getClass()); //$NON-NLS-1$
    
                Object principal = auth.getPrincipal();
    
                if ((principal != null) && logger.isDebugEnabled())
                    logger.debug("Principal of " + principal.getClass()); //$NON-NLS-1$
    
                if (principal instanceof InetOrgPerson)
                    return (InetOrgPerson) principal;
            }
            return null;
        }
    
        /*
         * ----------------------------------------------------------------------------------------------
         * The following stuff makes it more flexible, but you might prefer the short 'hardcoded' version
         * ----------------------------------------------------------------------------------------------
         */
        /**
         * @return the fullNameAttribute
         */
        public String getFullNameAttribute()
        {
            return fullNameAttribute;
        }
    
        /**
         * The name of the {@link InetOrgPerson} property (an LDAP attribute) to use for the
         * {@link User#fullName}. Defaults to "displayName"
         *
         * @param fullNameAttribute the fullNameAttribute to set
         */
        @SuppressWarnings("javadoc")
        public void setFullNameAttribute(String fullNameAttribute)
        {
            if (fullNameAttribute != null)
                this.fullNameAttribute = fullNameAttribute;
        }
    
        /**
         * @return the emailAddressAttribute
         */
        public String getEmailAddressAttribute()
        {
            return emailAddressAttribute;
        }
    
        /**
         * The name of the {@link InetOrgPerson} property (an LDAP attribute) to use for the
         * {@link User#emailAddress} Defaults to "mail"
         *
         * @param emailAddressAttribute the emailAddressAttribute to set
         */
        @SuppressWarnings("javadoc")
        public void setEmailAddressAttribute(String emailAddressAttribute)
        {
            if (emailAddressAttribute != null)
                this.emailAddressAttribute = emailAddressAttribute;
        }
    
        /**
         * Generic (String) getter from
         * https://stackoverflow.com/questions/14374878/using-reflection-to-set-an-object-property
         *
         * @param object Java object
         * @param fieldName property
         * @return result of the getter
         */
        @SuppressWarnings({ "nls" })
        protected static String getString(Object object, String fieldName)
        {
            Class<?> clazz = object.getClass();
            while (clazz != null)
            {
                try
                {
                    Field field = clazz.getDeclaredField(fieldName);
                    field.setAccessible(true);
                    return (String) field.get(object);
                }
                catch (NoSuchFieldException e)
                {
                    if (logger.isDebugEnabled())
                        logger
                                .debug("Check your bean configuration! NoSuchFieldException with getter of property '"
                                        + fieldName + "' of object of " + clazz);
                    clazz = clazz.getSuperclass();
                }
                catch (Exception e)
                {
                    if (logger.isDebugEnabled())
                        logger
                                .debug("Exception with getter of property '" + fieldName + "' of object of " + clazz);
                    logger.debug(e.getStackTrace());
                    clazz = clazz.getSuperclass();
                }
            }
            return null;
        }
    }
    

    Prepare for JasperServer updates

    Save the following files to another directory. In case of an update of the JasperServer, you copy the "To copy" files into your new installation and apply the settings from the "As reference" files.

    To copy

    WEB-INF/applicationContext-externalAuth-LDAP.xml

    WEB-INF/lib/jasperserverX-externalUserSetup.1.0.jar

    As reference

    WEB-INF/js.config.properties

    WEB-INF/log4j.properties


    Strange jasperserver.log entries you can ignore (or almost, AFAIK)

    Logfile-entry: Current system user can't modify keystore.

    You see entries similar to this one in the jasperserver.log

    • WARN KeystoreManager(Initialization),localhost-startStop-1:54 - Current system user can't modify keystore.

    Solution

    • Allow the group tomcat8 to access the key-files of the system user you use:
      • sudo chgrp tomcat8 ~/.jrsks*

    Logfile-entry: OperationNotSupportedException: Context is read only

    You see entries similar to this one in the jasperserver.log

    • ERROR JNDIResourceProvider,localhost-startStop-1:75 - error closing context javax.naming.OperationNotSupportedException: Context is read only

    This seems to be a very old bug and most probably you can just ignore it

    Known issues

    Session Timeout should be increased

    By default, the session timeout is set to 20 minutes in jasperserver/WEB-INF/web.xml. Increase this value to something fitting your environment, e.g. 8 hours

        <session-config>
            <!-- Default to 20 minute session timeouts -->
            <!-- Changed to 8h = 480min to prevent strange user experience -->
            <session-timeout>480</session-timeout>
        </session-config>

    See e.g. https://community.jaspersoft.com/wiki/how-configure-session-timeout-jasperreports-server

    • Explanation
      • If a user stays inactive in JasperServer for a longer time than defined in the session-timeout, the session will be invalidated and the user must login again (JasperServer default behavior)
      • If the user
        • has an open report in the browser and
        • just changes the options after the session timed out and
        • press 'Apply'
        • => Since the session is invalid, the report reloads including a Kerberos-Login, and the options are reset
      • This results in a strange experience for the users and should be circumvented by setting the session timeout value to e.g. 8h (the daily working hours). That is, the user will login automatically by Kerberos on every morning, but the session will remain active during the day.

    Test with a JasperServer installation with all internal components

    Note 2022-10-27: Not checked with update!

    I strongly suggest to set up an test VM with a JasperServer using internal components only! Then you can focus on getting the user authentication run.

    Problem with OpenJDK 11 (vs. OpenJDK 8) when using system Tomcat 8

    Note 2022-10-27: Not checked with update!

    With OpenJDK 11 installed, JasperServer is unable to start. This seems to be related to an old hibernate-validator-5.2.2.Final.jar, which throws an exception on checking the Java version with OpenJDK 11.

    I haven't checked, if this is the only jar incompatible with OpenJDK 11, but would use OpenJDK 8 exclusively for the time being.

    Access with JasperServer Studio

    Note 2022-10-27: Not checked with update!

    AFAIK, the Kerberos-authenticated access doesn't work with JasperStudio, neither the LDAP-based access with externally defined users (might differ in other environments).

    Using an internal user via Apache gives me no access, so IMHO the only viable solution seems to be to access Tomcat on port 8080 directly with an internal user.

    From a remote host, this will only work if you remove the IP-restriction on this port, so remove or change the 'address="127.0.0.1"'-attribute:

    Change conf/server.xml in <Service name="Catalina">

    <Connector port="8080" URIEncoding="UTF-8" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
    <!-- removed: address="127.0.0.1" -->
    

    Then use the URL http://jasperserver.domain.com:8080/jasperserver/services/repository in the Studio.

    See https://community.jaspersoft.com/wiki/connect-jaspersoft-studio-jasperreports-server for more information.

    Open

    Note 2022-10-27: No plans to work on this in the foreseeable future

    • How do I install the nice Tomcat ROOT webapp with the index.jsp as provided by JasperReports Server with the system package manager (in only installs the index.html ROOT version)?
    • How do I easily allow certain LDAP-users access to the Tomcat-Management (instead of using static users in conf/tomcat-users.xml)?

    User Feedback

    Recommended Comments

    As of 2022-10-27 this description is tested with JasperReports Server 8.1.0

    Search for 'Note 2022-10-27' and especially 'Note 2022-10-27 Changes from 7.1.0 to 8.1.0' to find the changes, only

     

    Link to comment
    Share on other sites

    Hi,

    thank you for your information!

    The issue with the hibernate JAR was taken over from the old description from 2018 and I haven't checked if it's still an issue. This applies to all issues below "Note 2022-10-27: Not checked with update!", but to make it clearer I added it to each of them.

    Link to comment
    Share on other sites



    Create an account or sign in to comment

    You need to be a member in order to leave a comment

    Create an account

    Sign up for a new account in our community. It's easy!

    Register a new account

    Sign in

    Already have an account? Sign in here.

    Sign In Now

×
×
  • Create New...