<table role="table">
<thead>
<tr>
<th>Q</th>
<th>A</th>
</tr>
</thead>
<tbody>
<tr>
<td>Bug report?</td>
<td>no</td>
</tr>
<tr>
<td>Feature request?</td>
<td>yes</td>
</tr>
<tr>
<td>BC Break report?</td>
<td>no</td>
</tr>
<tr>
<td>RFC?</td>
<td>no</td>
</tr>
<tr>
<td>Symfony version</td>
<td>3.2.0</td>
</tr>
</tbody>
</table>
<p dir="auto">I am implementing LDAP authentication using <code class="notranslate">form_ldap_bind</code> which supports the <code class="notranslate">search_dn</code> configuration option to dynamically generate the DN to use for the <code class="notranslate">ldap_bind()</code> check.</p>
<p dir="auto">However in my case the DN's differentiate on more than the username for different users:<br>
<code class="notranslate">uid=foo,ou=External,ou=Employees,dc=example,dc=com</code> vs. <code class="notranslate">uid=bar,ou=Internal,ou=Employees,dc=example,dc=com</code></p>
<p dir="auto">The <code class="notranslate">LdapUserProvider</code> in turn supports searching for users but then requires a password attribute to validate the authentication which isn't available in my case. <code class="notranslate">LdapBindAuthenticationProvider</code> also does not support a filter parameter (which might be useful here, but I am not an LDAP expert).</p>
<p dir="auto">I wonder if we need another auth provider which is more flexible to find the relevant DN from the LDAP server before attempting to validate the password via <code class="notranslate">ldap_bind()</code> along the lines of:</p>
<div class="snippet-clipboard-content notranslate position-relative overflow-auto" data-snippet-clipboard-copy-content="ldapsearch -d 1 -w 'XXX' -H ldaps://example.com:636 -D &quot;uid=sys,ou=Accounts,dc=example,dc=com&quot; -b 'dc=example,dc=com' '(uid=foo)' dn"><pre class="notranslate"><code class="notranslate">ldapsearch -d 1 -w 'XXX' -H ldaps://example.com:636 -D "uid=sys,ou=Accounts,dc=example,dc=com" -b 'dc=example,dc=com' '(uid=foo)' dn
</code></pre></div>
<p dir="auto">In implementing LDAP authentication in 2.8, we have found that authentication is limited to only users whose DNs fit the configured dn_string pattern. Let us consider this configuration:</p>
<div class="highlight highlight-source-yaml notranslate position-relative overflow-auto" dir="auto" data-snippet-clipboard-copy-content="security:
    providers:
        in_memory:
            memory: ~
        app_users:
            ldap:
                service: app.ldap
                base_dn: %ldap.base_dn%
                search_dn: null
                search_password: null
                filter: (uid={username})
                default_roles: ROLE_USER
    firewalls:
        secure:
            provider:  app_users
            stateless: true
            pattern:   ^/secure
            http_basic_ldap:
                service: app.ldap
                dn_string: &quot;uid={username},ou=people,dc=example,dc=com&quot;
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: ~"><pre class="notranslate"><span class="pl-ent">security</span>:
    <span class="pl-ent">providers</span>:
        <span class="pl-ent">in_memory</span>:
            <span class="pl-ent">memory</span>: <span class="pl-c1">~</span>
        <span class="pl-ent">app_users</span>:
            <span class="pl-ent">ldap</span>:
                <span class="pl-ent">service</span>: <span class="pl-s">app.ldap</span>
                <span class="pl-ent">base_dn</span>: <span class="pl-s">%ldap.base_dn%</span>
                <span class="pl-ent">search_dn</span>: <span class="pl-c1">null</span>
                <span class="pl-ent">search_password</span>: <span class="pl-c1">null</span>
                <span class="pl-ent">filter</span>: <span class="pl-s">(uid={username})</span>
                <span class="pl-ent">default_roles</span>: <span class="pl-s">ROLE_USER</span>
    <span class="pl-ent">firewalls</span>:
        <span class="pl-ent">secure</span>:
            <span class="pl-ent">provider</span>:  <span class="pl-s">app_users</span>
            <span class="pl-ent">stateless</span>: <span class="pl-c1">true</span>
            <span class="pl-ent">pattern</span>:   <span class="pl-s">^/secure</span>
            <span class="pl-ent">http_basic_ldap</span>:
                <span class="pl-ent">service</span>: <span class="pl-s">app.ldap</span>
                <span class="pl-ent">dn_string</span>: <span class="pl-s"><span class="pl-pds">"</span>uid={username},ou=people,dc=example,dc=com<span class="pl-pds">"</span></span>
        <span class="pl-ent">dev</span>:
            <span class="pl-ent">pattern</span>: <span class="pl-s">^/(_(profiler|wdt)|css|images|js)/</span>
            <span class="pl-ent">security</span>: <span class="pl-c1">false</span>
        <span class="pl-ent">main</span>:
            <span class="pl-ent">anonymous</span>: <span class="pl-c1">~</span></pre></div>
<p dir="auto">In this case, a user with DN "uid=foo,ou=people,dc=example,dc=com" can authenticate but one with DN "uid=foo,ou=something,ou=people,dc=example,dc=com" cannot, even though both accounts are in the configured search base.</p>
<p dir="auto">The immediate reason is that after discovering information from LDAP with a call to -&gt;find() in the LdapUserProvider::loadUserByUsername() method, the DN is discarded when LdapUserProvider::loadUser() creates the User instance.</p>
<p dir="auto">Because the DN is discarded, later LdapBindAuthenticationProvider::checkAuthentication() recombines the basic username with what is configured in dn_string:</p>
<p dir="auto">$dn = str_replace('{username}', $username, $this-&gt;dnString);</p>
<p dir="auto">It would seem if User were extended to store the DN (perhaps by creating LdapUser with a setDn() method), it would not be necessary to set dn_string, and users in various OU's could authenticate.</p>