// Path: core/domain/src/main/java/org/hesperides/core/domain/security/entities/springauthorities/DirectoryGroupDN.java
// public class DirectoryGroupDN implements GrantedAuthority {
// 
//     private final String authority;
// 
//     public DirectoryGroupDN(String authority) {
//         this.authority = authority;
//     }
// 
//     @Override
//     public String getAuthority() {
//         return authority;
//     }
// 
//     public static String extractCnFromDn(String dn) {
//         String cn = null;
//         try {
//             LdapName ldapName = new LdapName(dn);
//             for (Rdn rdn : ldapName.getRdns()) {
//                 if (rdn.getType().equalsIgnoreCase("CN")) {
//                     cn = (String) rdn.getValue();
//                 }
//             }
//         } catch (InvalidNameException e) {
//             throw new IllegalArgumentException("Invalid DN: " + dn, e);
//         }
//         if (cn == null) {
//             throw new IllegalArgumentException("Can't find CN in DN: " + dn);
//         }
//         return cn;
//     }
// }
// 
// Path: core/infrastructure/src/main/java/org/hesperides/core/infrastructure/security/LdapConfiguration.java
// @Profile(LDAP)
// @Component
// @ConfigurationProperties(prefix = "ldap")
// @Getter
// @Setter
// @Validated
// public class LdapConfiguration {
//     @NotEmpty
//     private String url;
//     
//     private String domain;
// 
//     private String bindDn;
// 
//     private String bindPassword;
//     @NotEmpty
//     private String userSearchBase;
//     @NotEmpty
//     private String roleSearchBase;
//     @NotEmpty
//     private String usernameAttribute;
//     @NotEmpty
//     private String connectTimeout;
//     @NotEmpty
//     private String readTimeout;
//     @NotEmpty
//     private String prodGroupDN;
//     @NotEmpty
//     private String techGroupDN;
// 
//     public String getSearchFilterForCN(String username) {
//         return String.format("(%s=%s)", usernameAttribute, username);
//     }
// }


import com.evanlennick.retry4j.CallExecutor;
import com.evanlennick.retry4j.CallExecutorBuilder;
import com.evanlennick.retry4j.config.RetryConfig;
import com.evanlennick.retry4j.exception.RetriesExhaustedException;
import com.evanlennick.retry4j.exception.UnexpectedException;
import com.google.gson.Gson;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.extern.slf4j.Slf4j;
import org.hesperides.core.domain.security.entities.springauthorities.DirectoryGroupDN;
import org.hesperides.core.infrastructure.security.LdapConfiguration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import java.util.*;
import java.util.function.Supplier;
        else{
            if (ldapConfiguration.getDomain() != null && !ldapConfiguration.getDomain().isEmpty()){
                env.put(Context.SECURITY_PRINCIPAL, String.format("%s\\%s", ldapConfiguration.getDomain(), username));
            }
            else{
                env.put(Context.SECURITY_PRINCIPAL, username);
            }

            env.put(Context.SECURITY_CREDENTIALS, password);
        }

        try {
            DirContext dirContext = new InitialLdapContext(env, null);
            // ici dirContext ne contient que des infos relatives au serveur avec lequel la connexion vient d'être établie
            if (log.isDebugEnabled()) { // on évite ce traitement si ce n'est pas nécessaire
                log.debug("[buildSearchContext] dirContext: {}", gson.toJson(attributesToNative(dirContext.getAttributes("").getAll())));
            }
            return dirContext;
        } catch (AuthenticationException | OperationNotSupportedException cause) {
            throw new BadCredentialsException(messages.getMessage(
                    "LdapAuthenticationProvider.badCredentials", "Bad credentials"), cause);
        } catch (NamingException e) {
            log.error(e.getExplanation() + (e.getCause() != null ? (" : " + e.getCause().getMessage()) : ""));
            throw LdapUtils.convertLdapException(e);
        }
    }

    public HashSet<String> retrieveParentGroupDNs(String dn) {
        HashSet<String> parentGroupDNs = new HashSet<>();
        try {
// Path: core/domain/src/main/java/org/hesperides/core/domain/security/entities/springauthorities/DirectoryGroupDN.java
// public class DirectoryGroupDN implements GrantedAuthority {
// 
//     private final String authority;
// 
//     public DirectoryGroupDN(String authority) {
//         this.authority = authority;
//     }
// 
//     @Override
//     public String getAuthority() {
//         return authority;
//     }
// 
//     public static String extractCnFromDn(String dn) {
//         String cn = null;
//         try {
//             LdapName ldapName = new LdapName(dn);
//             for (Rdn rdn : ldapName.getRdns()) {
//                 if (rdn.getType().equalsIgnoreCase("CN")) {
//                     cn = (String) rdn.getValue();
//                 }
//             }
//         } catch (InvalidNameException e) {
//             throw new IllegalArgumentException("Invalid DN: " + dn, e);
//         }
//         if (cn == null) {
//             throw new IllegalArgumentException("Can't find CN in DN: " + dn);
//         }
//         return cn;
//     }
// }
// 
// Path: core/infrastructure/src/main/java/org/hesperides/core/infrastructure/security/LdapConfiguration.java
// @Profile(LDAP)
// @Component
// @ConfigurationProperties(prefix = "ldap")
// @Getter
// @Setter
// @Validated
// public class LdapConfiguration {
//     @NotEmpty
//     private String url;
//     
//     private String domain;
// 
//     private String bindDn;
// 
//     private String bindPassword;
//     @NotEmpty
//     private String userSearchBase;
//     @NotEmpty
//     private String roleSearchBase;
//     @NotEmpty
//     private String usernameAttribute;
//     @NotEmpty
//     private String connectTimeout;
//     @NotEmpty
//     private String readTimeout;
//     @NotEmpty
//     private String prodGroupDN;
//     @NotEmpty
//     private String techGroupDN;
// 
//     public String getSearchFilterForCN(String username) {
//         return String.format("(%s=%s)", usernameAttribute, username);
//     }
// }


// Path: core/infrastructure/src/main/java/org/hesperides/core/infrastructure/security/groups/LdapSearchContext.java
import com.evanlennick.retry4j.CallExecutor;
import com.evanlennick.retry4j.CallExecutorBuilder;
import com.evanlennick.retry4j.config.RetryConfig;
import com.evanlennick.retry4j.exception.RetriesExhaustedException;
import com.evanlennick.retry4j.exception.UnexpectedException;
import com.google.gson.Gson;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Timer;
import lombok.extern.slf4j.Slf4j;
import org.hesperides.core.domain.security.entities.springauthorities.DirectoryGroupDN;
import org.hesperides.core.infrastructure.security.LdapConfiguration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.dao.IncorrectResultSizeDataAccessException;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.support.DefaultDirObjectFactory;
import org.springframework.ldap.support.LdapUtils;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.core.SpringSecurityMessageSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;
import javax.naming.*;
import javax.naming.directory.*;
import javax.naming.ldap.InitialLdapContext;
import java.util.*;
import java.util.function.Supplier;

        else{
            if (ldapConfiguration.getDomain() != null && !ldapConfiguration.getDomain().isEmpty()){
                env.put(Context.SECURITY_PRINCIPAL, String.format("%s\\%s", ldapConfiguration.getDomain(), username));
            }
            else{
                env.put(Context.SECURITY_PRINCIPAL, username);
            }

            env.put(Context.SECURITY_CREDENTIALS, password);
        }

        try {
            DirContext dirContext = new InitialLdapContext(env, null);
            // ici dirContext ne contient que des infos relatives au serveur avec lequel la connexion vient d'être établie
            if (log.isDebugEnabled()) { // on évite ce traitement si ce n'est pas nécessaire
                log.debug("[buildSearchContext] dirContext: {}", gson.toJson(attributesToNative(dirContext.getAttributes("").getAll())));
            }
            return dirContext;
        } catch (AuthenticationException | OperationNotSupportedException cause) {
            throw new BadCredentialsException(messages.getMessage(
                    "LdapAuthenticationProvider.badCredentials", "Bad credentials"), cause);
        } catch (NamingException e) {
            log.error(e.getExplanation() + (e.getCause() != null ? (" : " + e.getCause().getMessage()) : ""));
            throw LdapUtils.convertLdapException(e);
        }
    }

    public HashSet<String> retrieveParentGroupDNs(String dn) {
        HashSet<String> parentGroupDNs = new HashSet<>();
        try {
            String cn = DirectoryGroupDN.extractCnFromDn(dn);