Skip to content

Commit

Permalink
ldap: Fix password prompt when configured only to populate data.
Browse files Browse the repository at this point in the history
Previously, the logic for determining whether to provide an LDAP
password prompt on the registration page was incorrectly including it
if any LDAP authentication was backend enabled, even if LDAP was
configured with the populate-only backend that is not responsible for
authentication (just for filling in name and custom profile fields).

We fix this by correcting the conditional, and add a test.

There's still follow-up work to do here: We may still end up
presenting a registration form in situations where it's useless
because we got all the data from SAML + LDAP.  But that's for a future
issue.

This fixes a bug reported in zulip#13275.
  • Loading branch information
timabbott committed Oct 17, 2019
1 parent 5fd0a12 commit d364891
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 2 deletions.
85 changes: 85 additions & 0 deletions zerver/tests/test_signup.py
Original file line number Diff line number Diff line change
Expand Up @@ -2567,6 +2567,91 @@ def test_ldap_registration_from_confirmation(self) -> None:
"[email protected]"],
result)

@override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.EmailAuthBackend',
'zproject.backends.ZulipLDAPUserPopulator',
'zproject.backends.ZulipDummyBackend'))
def test_ldap_populate_only_registration_from_confirmation(self) -> None:
password = "testing"
email = "[email protected]"
subdomain = "zulip"
ldap_user_attr_map = {'full_name': 'fn'}
mock_directory = {
'uid=newuser,ou=users,dc=zulip,dc=com': {
'userPassword': ['testing', ],
'fn': ['New LDAP fullname']
}
}
init_fakeldap(mock_directory)

with patch('zerver.views.registration.get_subdomain', return_value=subdomain):
result = self.client_post('/register/', {'email': email})

self.assertEqual(result.status_code, 302)
self.assertTrue(result["Location"].endswith(
"/accounts/send_confirm/%s" % (email,)))
result = self.client_get(result["Location"])
self.assert_in_response("Check your email so we can get started.", result)
# Visit the confirmation link.
from django.core.mail import outbox
for message in reversed(outbox):
if email in message.to:
confirmation_link_pattern = re.compile(settings.EXTERNAL_HOST + r"(\S+)>")
confirmation_url = confirmation_link_pattern.search(
message.body).groups()[0]
break
else:
raise AssertionError("Couldn't find a confirmation email.")

with self.settings(
POPULATE_PROFILE_VIA_LDAP=True,
LDAP_APPEND_DOMAIN='zulip.com',
AUTH_LDAP_BIND_PASSWORD='',
AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=users,dc=zulip,dc=com'):
result = self.client_get(confirmation_url)
self.assertEqual(result.status_code, 200)

# Full name should be set from LDAP
result = self.submit_reg_form_for_user(email,
password,
full_name="Ignore",
from_confirmation="1",
# Pass HTTP_HOST for the target subdomain
HTTP_HOST=subdomain + ".testserver")

self.assert_in_success_response(["We just need you to do one last thing.",
"New LDAP fullname",
"[email protected]"],
result)

# Verify that the user is asked for name
self.assert_in_success_response(['id_full_name'], result)
# Verify that user is NOT asked for its LDAP/Active Directory password.
# LDAP is not configured for authentication in this test.
self.assert_not_in_success_response(['Enter your LDAP/Active Directory password.',
'ldap-password'], result)
# If we were using e.g. the SAML auth backend, there
# shouldn't be a password prompt, but since it uses the
# EmailAuthBackend, there should be password field here.
self.assert_in_success_response(['id_password'], result)

# Test the TypeError exception handler
mock_directory = {
'uid=newuser,ou=users,dc=zulip,dc=com': {
'userPassword': ['testing', ],
'fn': None # This will raise TypeError
}
}
init_fakeldap(mock_directory)
result = self.submit_reg_form_for_user(email,
password,
from_confirmation='1',
# Pass HTTP_HOST for the target subdomain
HTTP_HOST=subdomain + ".testserver")
self.assert_in_success_response(["We just need you to do one last thing.",
"[email protected]"],
result)

@override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipLDAPAuthBackend',
'zproject.backends.ZulipDummyBackend'))
def test_ldap_registration_end_to_end(self) -> None:
Expand Down
10 changes: 8 additions & 2 deletions zerver/views/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
redirect_to_deactivation_notice, get_safe_redirect_to

from zproject.backends import ldap_auth_enabled, password_auth_enabled, \
ZulipLDAPExceptionOutsideDomain, email_auth_enabled
ZulipLDAPExceptionOutsideDomain, email_auth_enabled, ZulipLDAPAuthBackend

from confirmation.models import Confirmation, RealmCreationKey, ConfirmationKeyException, \
validate_key, create_confirmation_link, get_object_from_key, \
Expand Down Expand Up @@ -155,7 +155,13 @@ def accounts_register(request: HttpRequest) -> HttpResponse:
# go through this interstitial.
form = RegistrationForm({'full_name': ldap_full_name},
realm_creation=realm_creation)
require_ldap_password = True

# Check whether this is ZulipLDAPAuthBackend,
# which is responsible for authentication and
# requires that LDAP accounts enter their LDAP
# password to register, or ZulipLDAPUserPopulator,
# which just populates UserProfile fields (no auth).
require_ldap_password = isinstance(backend, ZulipLDAPAuthBackend)
break
except TypeError:
# Let the user fill out a name and/or try another backend
Expand Down

0 comments on commit d364891

Please sign in to comment.