Configuration of ldap with Active Directory

I have a few problems, while following documentation I wanted to achieve login through Active Directory domain account or directly from app created account. At this moment configuration allows me to login by AD account but not from app account. Could you please help me with it? I’ve also encountered a bug while deleting account created from LDAP, app can’t create a new one when this same user trying to login again. And while I’m giving roles to domain accounts in app, some ‘anonymous’ user delete them when user re-login to app.

application.properties:

jmix.ldap.urls = ldap://###/
jmix.ldap.activeDirectoryDomain = ###
jmix.ldap.baseDn = ###
jmix.ldap.managerDn = dc=###,dc=com
jmix.ldap.managerPassword = ###
jmix.ldap.userSearchFilter = (&(objectClass=user)(sAMAccountName={0}))
jmix.ldap.defaultRoles = ui-minimal
jmix.ldap.useActiveDirectoryConfiguration = true
jmix.ldap.synchronizeRoleAssignments = false

##########################################################################

@Component("sample_MyUserSynchronizationStrategy")
public class MyUserSynchronizationStrategy extends AbstractLdapUserDetailsSynchronizationStrategy<User> {

    @Override
    protected Class<User> getUserClass() {
        return User.class;
    }

    @Override
    protected void mapUserDetailsAttributes(User userDetails, DirContextOperations ctx) {
        userDetails.setFirstName(ctx.getStringAttribute("givenName"));
        userDetails.setLastName(ctx.getStringAttribute("sn"));
        userDetails.setActive(true);
        userDetails.setVersion(1);
        userDetails.setPassword(ctx.getStringAttribute("unicodePwd"));
        userDetails.setEmail(ctx.getStringAttribute("mail"));
    }
}

Could someone of team answer me please?

Hi, @marcin.bohm30

Thank you for trying out the add-on!

Could you please clarify, if i got it right: when you enable LDAP authentication, you have trouble with login with users stored in the application? If yes, then you need to explicitly define a list of such users (the ones that should always be authenticated with the standard mechanism). This list is defined in jmix.ldap.standardAuthenticationUsers property.

What kind of error occurs in this case?

Do you mean that you manually assign some additional roles to users in addition to the ones that was assigned automatically during the synchronization?

I also don’t quite understand why you store user password in UserDetails? I guess that the value of the password is already encoded and when you store it in DB, it is encoded again by the Spring’s PasswordEncoder.

Regards,
Gleb

1 Like

Thanks for answer Gleb! :smiley:

Well, yes that’s what I meant. So is there any way to put list of users stored in app to this property?

jmix.ldap.standardAuthenticationUsers = Dev1, admin ← For example that worked but it’s hard to manage application if you need to add here every new user from outside of AD :wink:

Nvm, I something screwed up in configuration, now everything works fine.

Yeah, correctly. I added jmix.ldap.synchronizeRoleAssignments = false and jmix.ldap.userDetailsSource = app. And this problem has gone.

The main idea of this is like in Windows. At first logon you need to have connection with domain and the encoded password stores in database in cause you can’t connect to your database.

I didn’t found any way to do it :confused:

Is there some way to create new constructor for LdapProperties? Because there is no setters in this class and its hard to implement string list from some query. Thats why I need to create new constructor which overrides the one created. I found in UserDetailsServiceLdapUserDetailsMapper method setLdapProperties() but I have no idea how to implement into it new constructor.

I don’t really understand why you want to add AD users to this list. This list is intended to exclude some users from LDAP authentication (e.g. administrator users, which should have access to an application, regardless of the state of LDAP connection). Do I understand correctly that you don’t need LDAP authentication and you want just to obtain users from Active Directory?

Yes, that’s what I meant. If there is some way to obtain all users from AD and login them as standard authentication it would be wonderful. Because at this moment I need to provide list of users which are not in Active Directory but were created in the application, to the property: jmix.ldap.standardAuthenticationUsers(EXAMPLE OF LIST BELOW)

private EntityManagerFactory emf;

    public List<User> getUserList() {
        EntityManager em = emf.createEntityManager();

        TypedQuery<User> query = em.createQuery("select u from User u where u.password is not null ", User.class);
        return query.getResultList();
    }

    public List<String> standardAuthenticationUsers = getUserList().stream().map(User::getUsername).collect(Collectors.toList());

In this case you can just set up a synchronization of users from AD and disable LDAP authentication.
The add-on provides the LdapUserSynchronizationManager bean intended for synchronization of users from a particular LDAP group. If functionality of this bean doesn’t exactly fit your needs, you can reuse its code and create your own bean for synchronization. In the end you can set up a scheduled task that would obtain updated data from Active Directory.
But, as I said before, be aware that Active Directory stores password in encrypted form. So you won’t be able to just copy passwords from AD and use AD password for login with the standard authentication.
Finally, you can disable LDAP authentication by setting the corresponding property.

Regards,
Gleb

1 Like

I created something like this and it works fine for me. Gained a lot of experience during resolving this problem. Thanks for help :wink:

@Autowired
LdapProperties ldapProperties;

@Autowired
EntityManagerFactory entityManagerFactory;

public void addAppUsersWithStandardAuth() {
    EntityManager entityManager = entityManagerFactory.createEntityManager();
    TypedQuery<User> query = entityManager.createQuery("select u from User u where u.domain is null", User.class);
    List<String> getUserListToString = query.getResultList().stream()
            .map(User::getUsername)
            .collect(Collectors.toList());

    if (!ldapProperties.getStandardAuthenticationUsers().equals(getUserListToString)) {
        List<String> difference = getUserListToString.stream()
                .filter(element -> !ldapProperties.getStandardAuthenticationUsers().contains(element))
                .collect(Collectors.toList());
        ldapProperties.getStandardAuthenticationUsers().addAll(difference);
    }
}