Skip to content

[Security] Added LDAP support to Authenticator system #36600

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 3, 2020

Conversation

wouterj
Copy link
Member

@wouterj wouterj commented Apr 27, 2020

Q A
Branch? master
Bug fix? no
New feature? yes
Deprecations? no
Tickets -
License MIT
Doc PR -

The last missing authenticator in the new system 🎉

I have no experience with LDAP at all and I didn't succeed in setting up a server locally. So I can't test whether this works, but the unit test works (and also tested in a real app, while adding a dd() call in the listener).


I want to share with you the current state of Security LDAP, how this PR implements it and a possible other solution (which I think I would prefer most). Is there anyone who can share their opinions on this? (hopefully @weaverryan and @csarrazi can share their opinion, as they have most experience on this topic)

  1. Current Solution: An LDAP authentication provider + duplicated SecurityFactory classes
    LDAP is done in one centralized authentication provider. This provider is configured by security factories for each core factory (e.g. form_login becomes form_login_ldap, http_basic becomes http_basic_ldap).

  2. Implementation in this PR: A listener is executed before the default VerifyCredentialsListener, to verify PasswordCredentials
    This listener must be configured for each specific authenticator wanting to use LDAP. This is a technique similar to (1). It's a bit difficult to use this for your own authenticator (you need to configure a custom listener service) and still needs the duplicated factory classes

  3. Proposal: Introduce a LdapCredentials class and always register a listener
    If an authentictor returns LdapCredentials, it'll be checked using the LDAP verification listener. This is the easiest for custom authenticators and would remove the duplicated factories, I can imagine form_login getting a new ldap sub option to configure the settings.

    The main disadvantage (I think) is that we would need to make LdapCredentials configure all options: ldap service, dnString, searchDn, searchPassword & queryString. Especially passing around the ldap service seems a bit weird. The main questions here are: Is it weird to pass all these things in the LdapCredentials? And, do we really need to support having multiple LDAP configuration sets for different authenticators? Or can we e.g. add a global security.ldap configuration, that registers the listener for all authenticators returning LdapCredentials?

@weaverryan
Copy link
Member

After chatting about this, here is my proposal:

  1. Could we decorate FormLoginAuthenticator with an LdapAuthenticator? The decorator would call the inner authenticate() but then ADD an LdapCredentialsBadge (not a "credentials" badge, it would just hold LDAP configuration, including the LDAP service id, but not the actual service object

  2. Register a listener, like VerifyLdapCredentialsListener (higher priority than VerifyCredentialsListener ) that looks for the LdapCredentialsBadge. If found, it would use the password from the PasswordCredentials and the config from the LdapCredentialsBadge to do the credentials checking. To avoid passing the Ldap service around in the LdapCredentialsBadge, we could inject a service locator into the listener. It would contain all LDAP services that were registered for any ldap authenticators. So... in reality, it would contain a maximum of 2 - one for the service configured under http_basic_ldap and one for the service configured under form_login_ldap.

For the last part, we would leverage a new tag for ldap: if your ldap service has a tag, it's injected into the listener's locator. This will allow people to write custom authenticators that use LDAP. They would just need to:

A) Register the LDAP service
B) Give it the new tag we invent
C) Return PasswordCredentials and LdapCredentialsBadge

@nicolas-grekas nicolas-grekas added this to the next milestone May 1, 2020
@wouterj wouterj force-pushed the security/new-system-ldap branch 2 times, most recently from 06d65ef to e71a5a3 Compare May 2, 2020 12:40
@wouterj wouterj marked this pull request as ready for review May 2, 2020 12:40
@wouterj wouterj force-pushed the security/new-system-ldap branch 6 times, most recently from 3f0e4d9 to f3b5ca6 Compare May 3, 2020 15:04
$ldapBadge = $passport->getBadge(LdapBadge::class);
if ($ldapBadge->isResolved()) {
return;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess technically this should be moved before the if statement above? In theory, someone could resolve this and not even have a PasswordCredentials? Not sure why, but I think it makes sense higher.


/** @var PasswordCredentials $passwordCredentials */
$passwordCredentials = $passport->getBadge(PasswordCredentials::class);
if ($passwordCredentials->isResolved()) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would mean that LdapBadge is NOT resolved, but PasswordCredentials IS resolved. I would prefer to throw an exception: this is an unexpected case: a resolved PasswordCredentials is basically the same (at this point) as NO PasswordCredentials since a resolved PasswordCredentials has a null password.

@weaverryan weaverryan force-pushed the security/new-system-ldap branch from 2c49273 to 20962e6 Compare May 3, 2020 16:57
Copy link
Member

@weaverryan weaverryan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is ready - test failures are unrelated

@fabpot
Copy link
Member

fabpot commented May 3, 2020

Thank you @wouterj.

@fabpot fabpot merged commit 09645a9 into symfony:master May 3, 2020
@wouterj wouterj deleted the security/new-system-ldap branch May 3, 2020 21:09
@nicolas-grekas nicolas-grekas modified the milestones: next, 5.1 May 4, 2020
@fabpot fabpot mentioned this pull request May 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants