Payara Server LDAP Integration – Part 2: Configuring Security

Uncategorized

See ‘Part 1 – Configuring the LDAP Server’ here.

 

In this three-parts article series I will illustrate the implementation of the LDAP integration using a sample scenario: integrate Payara Server with a LDAP user directory and manage the authentication and authorization of a sample web application.

 

 

In Part 1, I showed you how to start the LDAP Server – now we need to configure a new LDAP security realm in our Payara Server instance for our Java EE application to connect to the user directory through the JAAS (Java Authentication and Authorization Services) API. With a Payara Server domain running, we execute the following command:

create-auth-realm --classname=com.sun.enterprise.security.auth.realm.ldap.LDAPRealm 
--property=jaas-context=ldapRealm:
base-dn="dc=payara, dc=fish":
directory="ldap://192.168.99.100:1389"
group-search-filter="member=%d"
--target=server-config userDirectoryRealm

 

With this command, we’re creating a new LDAP security realm called userDirectoryRealm that will authenticate and authorize both users and groups for our Java EE web application. You will notice that we are setting the following properties:

  • Base DN: We set this property to the base directory name of our LDAP, in this case dc=payara, dc=fish.
  • Directory: This property points to the location of our LDAP server. Pay attention to the port that we are using (1389).
  • Group Search Filter: With this property, we’re overriding the default search filter query that the realm uses to identify which users are part of a group. Since OpenDJ uses the object attribute member,  we’re setting the query to use it instead of the default configured attribute (uniquemember).

What happens if you want to fine tune the directory searches and filters specifically for your organization? You can use the following additional properties:

  • group-base-dn: set this property to the base directory name – the realm will use it to read the group data. For example: cn=Organization Groups would imply that all the organization’s groups live in this directory object.
  • search-bind-dn: set this property to the directory name of an administrator user  the realm can bind to in case your LDAP server doesn’t allow anonymous binding (in our case, the default setting of OpenDJ is to indeed allow anonymous binding).
  • search-bind-password: the password of the DN set in the search-bind-dn property
  • search-filter: you can use this property to customize the search query used to locate a user in the directory tree. For example: ou=People, uid=%s would limit the search to all users under the People organization unit object.

Creating a Web Application

Next, we proceed to create a sample web application to test out our LDAP configuration. For this application, we will create 3 sample JSF pages:

 

1 – Our first page will be an index.xhtml landing page that will simply print out the UID/Username of the authenticated user:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>LDAP Test</title>
    </h:head>
    <h:body>
        Welcome #{welcomeBean.user}!
        <h:form>
            <h:commandLink value="Go to admins page" action="admin/index.xhtml?faces-redirect=true"/>
            <br/>
            <h:commandLink value="Go to commons page" action="common/index.xhtml?faces-redirect=true"/>
        </h:form>
    </h:body>
</html>

 

Notice that we are using a bean of name WelcomeBean to get the username (more on this bean later). We also are setting some navigation links to access the other two pages.

 

2 – Another page, admin/index.xhtml (under folder admin), that only users that belong to the Admins group can access:

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:h="http://xmlns.jcp.org/jsf/html">
<h:head>
<title>For Admins Only</title>
</h:head>
<h:body>
Welcome #{welcomeBean.user}! Since you are an administrator, you can access this page
</h:body>
</html>

 

3 – A final common/index.xhtml page (under folder common) that both common users and administrators can access:

 

<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html">
    <h:head>
        <title>For Everyone Actually</title>
    </h:head>
    <h:body>
        Welcome #{welcomeBean.user}! You are in the common group, so you can see this page.
    </h:body>
</html>

 

4 – Finally, we create our WelcomeBean CDI component to get the username:

 

@RequestScoped
@Named
public class WelcomeBean {

    @Inject
    Principal principal;

    public String getUser(){
        return principal.getName();
    }
}

 

 

We are retrieving the username with an injected Principal object courtesy of the CDI-JAAS bridge integration included with Java EE.

 

Now that we have our code artifacts, we will proceed to configure our application’s security constraints. First, we configure authorization access for these pages and authentication in the web.xml file:

 

<?xml version="1.0" encoding="UTF-8"?>
<web-app version="3.1" xmlns="http://xmlns.jcp.org/xml/ns/javaee" 
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee 
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd">
    <context-param>
        <param-name>javax.faces.PROJECT_STAGE</param-name>
        <param-value>Development</param-value>
    </context-param>
    <servlet>
        <servlet-name>Faces Servlet</servlet-name>
        <servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>Faces Servlet</servlet-name>
        <url-pattern>*.xhtml</url-pattern>
    </servlet-mapping>
    <welcome-file-list>
        <welcome-file>index.xhtml</welcome-file>
    </welcome-file-list>
    <security-constraint>
        <display-name>General Security</display-name>
        <web-resource-collection>
            <web-resource-name>All</web-resource-name>
            <url-pattern>/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
            <role-name>COMMON_USER</role-name>
        </auth-constraint>
    </security-constraint>
    <security-constraint>
        <display-name>Only for administrators</display-name>
        <web-resource-collection>
            <web-resource-name>Admin</web-resource-name>
            <url-pattern>/admin/*</url-pattern>
        </web-resource-collection>
        <auth-constraint>
            <role-name>ADMIN</role-name>
        </auth-constraint>
    </security-constraint>
    <login-config>
        <auth-method>BASIC</auth-method>
        <realm-name>userDirectoryRealm</realm-name>
    </login-config>
    <session-config>
        <session-timeout>5</session-timeout>
    </session-config>
</web-app>

 

 

Notice the following:

  • In the login-config session we are configuring how our application will be authenticating users: using BASIC authentication against the LDAP realm we created. In a production environment, you may choose a more robust auth-method configuration since BASIC is an insecure option.
  • In the security-constraint section we are distributing the access to the 3 pages we created to the 2 roles ADMIN and COMMON_USER. Keep in mind that these 2 roles MUST be mapped to the corresponding groups that are registered in the LDAP server.

Finally, we configure the role mappings in the glassfish-web.xml file:

 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE glassfish-web-app PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Servlet 3.0//EN" "http://glassfish.org/dtds/glassfish-web-app_3_0-1.dtd">
<glassfish-web-app error-url="">
  <class-loader delegate="true"/>
  <context-root>/ldap-test</context-root>
  <security-role-mapping>
    <group-name>Admins</group-name>
    <role-name>ADMIN</role-name>
  </security-role-mapping>
  <security-role-mapping>
    <group-name>Common</group-name>
    <role-name>COMMON_USER</role-name>
  </security-role-mapping>
</glassfish-web-app>

 

With this configuration, whenever Payara Server checks the authorization access permissions of a user against the LDAP server, it will use the mappings defined to check the correct group based on its roles.

 

Now that we have configured our application, we can just simply build our WAR file and deploy it to Payara Server (I recommend using Maven for this step). To test that our scenario works as intended we head to the landing page first (the browser will ask for credentials so we will input cbeta/cbeta):

 

2-ldap-web-test-1.jpg

 

 

Now, if we navigate to the administrator’s page:

 

3-ldap-web-test-2.jpg

 

Since user cbeta doesn’t belong to the Admins group, the server will deny access to the page by responding with a 403 Forbidden error page. This is to be expected in accordance to the security constraints set earlier.

 

If you execute the same tests with user malfa, then access will be granted to both pages:

 

4-ldap-web-test-3.jpg

 

5-ldap-web-test-4.jpg

See ‘Part 3 : Extracting User Information’ here.

 

 

Comments (10)

Post a comment

Your email address will not be published. Required fields are marked *

Payara needs the contact information you provide to us to contact you about our products and services. You may unsubscribe from these communications at any time. For information on how to unsubscribe, as well as our privacy practices and commitment to protecting your privacy, please review our Legal & Privacy Policy.

  1. Hitesh Tambe

    Hello Fabio,

    Thanks for the informative post.

    I have a requirement where we I need to establish LDAP based login to Payara server administration console.
    I have done the configuration in Security > Realms > LDAP, but I am still not able to login using ldap id.

    Could you please share what steps do I need to take in order to enable this?

    Thanks,
    Hitesh Tambe

    1. Jaya NS

      Hi, did you find the solution for this ? it would be helpful if we share the fix.

      1. Fabio Turizo

        Hi Jaya, check my reply to Hitesh, which unfortunately was posted in the original thread below. If you are still struggling with this issue, I’d recommend getting help in our official Payara Forum here: https://groups.google.com/forum/#!topic/payara-forum/.

        Cheers,
        Fabio.

  2. Francisco Flores

    Hi Fabio,
    I have a very confusing situation where the server “selectively” performs an ldap bind request for some users and not for others.. I’ve captured the traffic between the payara server and the ldap server … the sequence that I see in the “good” case is : searchRequest/Response/Done, bindRequest/Response, searchRequest/Response/Done (group membership) and finally another searchRequest/Response/Done for the user… however in the “bad” cases there is no bindRequest even though the first searchRequest is successful finding the user….

  3. Jadon Ortlepp

    Hi Francisco,
    You may find it worthwhile asking over at our google forum where hopefully someone will be able to help you out.
    https://groups.google.com/forum/#!forum/payara-forum

  4. Fabio Turizo

    Hi Hitesh,

    After creating the realm, you have to change the realm used by the Admin Service (by default ‘admin-realm’) and set it to the new realm that you have created. You can do this change in the Admin Console under Configurations -> Server Configuration -> Admin Service. Keep in mind that changing the default realm comes with associated risks since the server would have a direct dependeny on your LDAP server to work correctly.

    Cheers,
    Fabio.

  5. Alessandro Cosenza

    nice post! however how do you define the password mapping? in other words, the password given in the browser form must match with some password field in LDAP right?

  6. Fabio Turizo

    Hi Alessandro.

    On Part 1, I specified the initial set of users that will populate the directory. If you observe close enough, you will see that the user passwords are specified as hashed values by using the standard LDAP property `userPassword`. The LDAP specification dictates that any authorization attempts must match against what the configuration of the password is. I recommend reading the documentation on how password storage works for OpenLDAP here: https://www.openldap.org/doc/admin24/security.html

    Cheers,
    Fabio.

  7. Marcel Florin Marcus

    Fabio, sharing the sources is the best way of sharing knowledge.

  8. Fabio Turizo

    Hi Marcel, unfortunately, this post is a bit old and I no longer have access to the sources. Replicating the entire codebase following the instructions in the 2 parts should be simple enough.

Related Posts

Payara Qube-Cloud Light banner 4 minutes
Security

Zero Trust Security in Enterprise Java: What it is and How to Implement it

Cybersecurity isn’t just about building walls, fortresses, moats or any other external barrier anymore. Nowadays, it’s important to check […]

4 minutes
Uncategorized

Leading the Way: Payara Platform Community 7 Beta Now Fully Jakarta EE 11 Certified

We’re excited to announce that Payara Platform Community 7 Beta application server is now fully certified as Jakarta EE 11 […]

Understanding the Security Issues of Aging Middleware 8 minutes
Security

Understanding the Security Issues of Aging Middleware

Middleware runs quietly in the background of most applications, which makes it easy to overlook its lifecycle. In effect, […]