LDAP CRUD Operation Example – Spring Boot

In the previous article, we learned how to set up LDAP with the spring boot application and how to retrieve the LDAP record using LdapTemplate. In this article, we will learn how to perform CRUD operations on LDAP data with the spring boot application.

Also, we will perform the LDAP CRUD operation with the help of the LdapTemplate.

We have used the Spring boot version: 2.2.0.RELEASE and Java version: 1.8 for this application development.

Table of Contents

Create Spring boot application with required dependencies

Create a Spring boot application with required dependencies.

Also, add spring-boot-starter-web, spring-boot-starter-data-ldap, Lombok(to reduce boilerplate code), and unboundid-ldapsdk dependencies to the application.

Finally, the below is the pom.xml with all the required dependencies.

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.0.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.asb.example</groupId>
    <artifactId>spring-embedded-ldap</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-embedded-ldap</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <maven-jar-plugin.version>3.1.1</maven-jar-plugin.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-ldap</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.unboundid</groupId>
            <artifactId>unboundid-ldapsdk</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

Once the Spring boot application is created with the required dependencies, the next step is to configure the application.

Add LDAP configuration properties

Let us add the specified LDAP configuration properties to the Spring boot application’s application.properties file.

#External LDAP directory config:
#spring.ldap.urls=ldap://localhost:389
#spring.ldap.base=ou=people,dc=maxcrc,dc=com
#spring.ldap.username=cn=Manager,dc=maxcrc,dc=com
#spring.ldap.password=secret

#Embedded LDAP server config:
spring.ldap.embedded.base-dn=dc=asb,dc=com
spring.ldap.embedded.credential.username=uid=admin
spring.ldap.embedded.credential.password=secret
spring.ldap.embedded.ldif=classpath:asb-ldap.ldif
spring.ldap.embedded.port=123
spring.ldap.embedded.validation.enabled=false

Add the LDIF file to load initial LDAP data

Create a .ldif file under /src/main/resources/ folder and add the below content.

dn: dc=asb,dc=com
objectclass: top
objectclass: domain
objectclass: extensibleObject
dc: asb
dn: ou=people,dc=asb,dc=com
objectclass: top
objectclass: organizationalUnit
ou: people
dn: uid=ben,ou=people,dc=asb,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: Anne Hathaway
sn: Hathaway
uid: anne
description: Pretty Girl!!
dn: uid=bob,ou=people,dc=asb,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: John Hamilton
sn: Hamilton
uid: john
description: Cool guy!!
dn: uid=asb,ou=people,dc=asb,dc=com
objectclass: top
objectclass: person
objectclass: organizationalPerson
objectclass: inetOrgPerson
cn: ASB Notebook
sn: Notebook
uid: asbnotebook
description: Technical blog!.

Perform the CRUD operation on LDAP records

Once the required configurations are ready, we can write code to perform CRUD operation on LDAP data.

Create a DTO class

Create a java class with the name Person. This DTO class will be used to hold the LDAP record during the CRUD operation.

package com.asb.example;

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;

@Getter
@Setter
@ToString
public class Person {
    private String userId;
    private String fullName;
    private String lastName;
    private String description;
}

Perform LDAP CRUD operation

The next step is to write a service layer, which performs CRUD operation on LDAP records using the Ldap Template.

Create an interface with the name PersonRepo, and add the required CRUD operation method signatures.

package com.asb.example;

import java.util.List;

public interface PersonRepo {
    public List<Person> retrieve();
    public String create(Person p);
    public String update(Person p);
    public String remove(String userId);
}

Create a java implementation class with the name PersonRepoImpl, and implement the methods defined in the PersonRepo interface.

package com.asb.example;

import static org.springframework.ldap.query.LdapQueryBuilder.query;
import java.util.List;
import javax.naming.Name;
import javax.naming.NamingException;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.SearchControls;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ldap.core.AttributesMapper;
import org.springframework.ldap.core.LdapTemplate;
import org.springframework.ldap.support.LdapNameBuilder;
import org.springframework.stereotype.Service;

@Service
public class PersonRepoImpl implements PersonRepo {

    public static final String BASE_DN = "dc=asb,dc=com";

    @Autowired
    private LdapTemplate ldapTemplate;

    @Override
    public String create(Person p) {
        Name dn = buildDn(p.getUserId());
        ldapTemplate.bind(dn, null, buildAttributes(p));
        return p.getUserId() + " created successfully";
    }

    @Override
    public String update(Person p) {
        Name dn = buildDn(p.getUserId());
        ldapTemplate.rebind(dn, null, buildAttributes(p));
        return p.getUserId() + " updated successfully";
    }

    @Override
    public String remove(String userId) {
        Name dn = buildDn(userId);
        // ldapTemplate.unbind(dn, true); //Remove recursively all entries
        ldapTemplate.unbind(dn);
        return userId + " removed successfully";
    }

    private Attributes buildAttributes(Person p) {
        BasicAttribute ocattr = new BasicAttribute("objectclass");
        ocattr.add("top");
        ocattr.add("person");
        Attributes attrs = new BasicAttributes();
        attrs.put(ocattr);
        attrs.put("uid", p.getUserId());
        attrs.put("cn", p.getFullName());
        attrs.put("sn", p.getLastName());
        attrs.put("description", p.getDescription());
        return attrs;
    }

    public Name buildDn(String userId) {
        return LdapNameBuilder.newInstance(BASE_DN).add("ou", "people").add("uid", userId).build();
    }

    public Name buildBaseDn() {
        return LdapNameBuilder.newInstance(BASE_DN).add("ou", "people").build();
    }

    @Override
    public List<Person> retrieve() {
        SearchControls searchControls = new SearchControls();
        searchControls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        List<Person> people = ldapTemplate.search(query().where("objectclass").is("person"),
                new PersonAttributeMapper());
        return people;
    }

    private class PersonAttributeMapper implements AttributesMapper<Person> {
        @Override
        public Person mapFromAttributes(Attributes attributes) throws NamingException {
            Person person = new Person();
            person.setUserId(null != attributes.get("uid") ? attributes.get("uid").get().toString() : null);
            person.setFullName(null != attributes.get("cn") ? attributes.get("cn").get().toString() : null);
            person.setLastName(null != attributes.get("sn") ? attributes.get("sn").get().toString() : null);
            person.setDescription(
                    null != attributes.get("description") ? attributes.get("description").get().toString() : null);
            return person;
        }
    }
}

This class contains the implementation of all CRUD operations on LDAP data using the LdapTemplate.

We are using bind(), rebind(), and unbind() methods of LdapTemplate to perform create, update and delete operations on LDAP records.

We have also used LdapNameBuilder to build the distinguished name(DN), which is required for the different operations.

Finally, we have created a private attribute mapper class called PersonAttributeMapper to map the LDAP records into Person DTO objects.

Expose RESTful CRUD endpoints

Finally, let us expose the RESTful endpoints for CRUD operation.

Create a Rest controller class called LdapBindController.java as shown below.

This class contains different REST endpoints, that supports CRUD operations.

package com.asb.example;

import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class LdapBindController {

    @Autowired
    private PersonRepo personRepo;

    @PostMapping("/add-user")
    public ResponseEntity<String> bindLdapPerson(@RequestBody Person person) {
        String result = personRepo.create(person);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    @PutMapping("/update-user")
    public ResponseEntity<String> rebindLdapPerson(@RequestBody Person person) {
        String result = personRepo.update(person);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }

    @GetMapping("/retrieve-users")
    public ResponseEntity<List<Person>> retrieve() {
        return new ResponseEntity<List<Person>>(personRepo.retrieve(), HttpStatus.OK);
    }

    @GetMapping("/remove-user")
    public ResponseEntity<String> unbindLdapPerson(@RequestParam(name = "userId") String userId) {
        String result = personRepo.remove(userId);
        return new ResponseEntity<>(result, HttpStatus.OK);
    }
}

Test the CRUD operation with Postman

It’s time to test the application!! 🙂 Launch the spring boot application.

In the below image, we can observe the LDAP records, imported from the LDIF file.

ldap crud spring boot

LDAP binding

The below image shows the create(bind) operation of a new record on the LDAP server.

ldap crud spring boot

Once we get the success message, we can verify it by retrieving the records.

LDAP retrieve with bind record

LDAP Rebind

The below image shows the update operation.

LDAP rebind example

Retrieve the records to confirm the record update.

LDAP retrieve with rebind data

LDAP Unbind

The below image shows delete operation by user-id(uid).

LDAP unbind example

Retrieve the records to confirm the record delete operation.

LDAP retrieve with unbind example

Conclusion

In this article, we learned how to perform CRUD operations on LDAP records using LdapTemplate, while developing the spring boot applications.

We learned the bind(), rebind(), and unbind() methods of the Ldap Template utility and how to use them to perform CRUD operations.

Example code is available on GitHub.

LDAP CRUD Operation Example – Spring Boot
Scroll to top

Discover more from ASB Notebook

Subscribe now to keep reading and get access to the full archive.

Continue reading