3

I'm currently trying to implement an simple CRUD integrated with Active Directory via LDAP, using Spring Data, for managing my internal users.

The problem is, while the reading works as expected, any writing on AD (creating or editing a user, for example) results in a generic error message, shown below:

[LDAP: error code 53 - 0000209A: SvcErr: DSID-031A107A, problem 5003 (WILL_NOT_PERFORM), data 0\n\u0000]; remaining name 'DC=company, DC=com'

The ldap connection is being made using LDAPS with an admin user. I can even work with the same credentials without any issues in a simple nodejs test application. So I'm probably making some mistake with Spring Data.

The relevant source code is attached below.

Entity class:

// Person.java (Entity model)
@Data
@Entry(
   base = "ou=Employees,dc=company,dc=com",
   objectClasses = {"person", "top"}
)
public class Person {

  @Id
  private Name dn;

  @Attribute(name = "cn")
  private String commonName;

  @Attribute(name = "sAMAccountName")
  private String accountName;

  @Attribute(name = "userPrincipalName")
  private String username;

  @Attribute(name = "mail")
  private String mail;

  @Attribute(name = "userPassword")
  private String password;

  @Attribute(name = "description")
  private String desc;

  @Attribute(name = "memberOf")
  private List<String> groups;

  @Attribute(name = "company")
  private String company;

  @Attribute(name = "objectClass")
  private List<String> objectClasses;

  @Attribute(name = "objectCategory")
  private String objectCategory;
}

Repository class:

// PersonRepository.java
@Repository
public interface PersonRepository extends LdapRepository<Person> {
    Person findByMailIgnoreCase(String mail);
}

Service class:

@Service
public class UserService {

   @Autowired
   private PersonRepository personRepository;

    /**
    * Save the user at AD.
    *
    * @param username       the user login name
    * @param name           the user name and surename
    * @param companyExtName the company external name
    * @param email          the user email
    * @param description    the user description
    * @return the newly created user
    */
    public Person createPerson(String username, String name, String companyExtName,
                            String email, String description) {

        final Person user = new Person();
        user.setAccountName(username);
        user.setCommonName(name);
        user.setCompany(companyExtName);
        user.setMail(email);
        user.setUsername(email);

        String tempPass = RandomStringUtils.randomAscii(10);
        user.setPassword(digestSHA(tempPass));
        user.setDn(LdapNameBuilder.newInstance("DC=company, DC=com")
                .build());

        List<String> objClasses = new ArrayList<>();
        objClasses.add("person");
        objClasses.add("top");
        user.setObjectClasses(objClasses);
        user.setObjectCategory("CN=Person,CN=Schema,CN=Configuration,DC=company,DC=com");

        List<String> groups = new ArrayList<>();
        groups.add("CN=Administrators,CN=Builtin,DC=company,DC=com");
        user.setGroups(groups);

        if (description != null && !description.isEmpty()) {
            user.setDesc(description);
        }

        return personRepository.save(user);
    }

    /**
    * Encodes the user password as it is used at Active Directory
    *
    * @param plain the plain text password
    * @return the password hash
    */
    private static String digestSHA(String plain) {
        try {
            MessageDigest digester = MessageDigest.getInstance("SHA-256");
            digester.update(plain.getBytes());
            return String.format("{SHA}%s", Base64.getEncoder().encodeToString(digester.digest()));
        } catch (NoSuchAlgorithmException ex) {
            return null;
        }
    }

The exception is thrown when I call personRepository.save(user);

As a addtional information, I've already tried a few variations of the code attached -- tried to remove almost all user data beforing saving it, different password encodings and hashing -- but the result is always the same. Any help on this will be greatly appreciated.

Thanks!

EDIT: Investigation indicates that the cause is probably something related with the way I'm sending my user DN. Anyway, I'm still wrestling with this issue.

catastrophic error
  • 380
  • 1
  • 5
  • 18

2 Answers2

1

I was able to create/edit my Active Directory users with a workaround. In my UserService, instead of using the Spring Data Ldap repository, I've used the LdapTemplate methods, like shown below.

// UserService.java
   public void createPerson() {
    Name userDn = LdapNameBuilder
            .newInstance()
            .add("ou", ou)
            .add("cn", accountName)
            .build();
    DirContextAdapter context = new DirContextAdapter(userDn);

    context.setAttributeValue("cn", accountName);
    context.setAttributeValue("sn", accountName);
    context.setAttributeValue("userPassword", digestSHA(password));
    context.setAttributeValue("company", company);
    context.setAttributeValue("description", desc);
    context.setAttributeValue("mail", mail);
    context.setAttributeValue("sAMAccountName", accountName);
    context.setAttributeValue("userPrincipalName", username);
    context.setAttributeValue("objectCategory", objectCategory);

    context.setAttributeValues("objectClass", objectClasses.toArray());
    DirContextAdapter context = user.getLdapContext("Users");
    ldapTemplate.bind(context);
 }

Since I used the same values for user creation with both Spring Data and LdapTemplate, my original issue is probably related to some treatment Spring does before sending the data to my Active Directory server.

Since the method above is currently working for me, I'll follow with it. When I have some spare time I'll go back to this to find out what I was doing wrong with Spring.

For future use, I believe it is related to memberOf attribute. This attribute must be set after the user is created, but it seems that Spring Data is filling this property with an empty string even if I set the attribute to null when creating the user.

catastrophic error
  • 380
  • 1
  • 5
  • 18
  • I think it is because of Default Attributes in Spring Data Ldap that they are not compatible with windows security policies. – taha Jan 14 '19 at 05:27
  • i also facing same issue. But for me the code was working for 2 years. then suddenly started throwing error. there was no new deployments. any ideas. For a password reset api, i use ldaptemplate which is working fine. but creation using LdapRepository is failing. – arvin_v_s Sep 07 '20 at 15:02
0

Lots of articles says that it is because of a lack of SSL connection to a LDAP server. Here are some links: stackoverflow.com/questions/17290539

stackoverflow.com/questions/6797955

forum.spring.io/forum/spring-projects/data/ldap

community.oracle.com/thread/2177638

Yuriy Tsarkov
  • 2,461
  • 2
  • 14
  • 28
  • Thanks for your response. Actually I'm currently working with a SSL connection (Ldaps over port 636). I'm still digging on this and so far it seems that is related with the way which spring data builds its queries. – catastrophic error Aug 20 '18 at 13:58