diff --git a/templates/zerver/emails/followup_day1.source.html b/templates/zerver/emails/followup_day1.source.html index e630dd49b2..ee989babd9 100644 --- a/templates/zerver/emails/followup_day1.source.html +++ b/templates/zerver/emails/followup_day1.source.html @@ -22,10 +22,14 @@

{{ _('Your account details:') }}

  • {{ _('Organization URL:') }} {{ realm_uri }}
  • - {% if ldap_username %} -
  • {{ _('Use your LDAP account to login') }}
  • + {% if ldap %} + {% if ldap_username %} +
  • {{ _('Username:') }} {{ ldap_username }}
  • + {% else %} +
  • {{ _('Use your LDAP account to login') }}
  • + {% endif %} {% else %} -
  • {{ _('Email:') }} {{ email }}
  • +
  • {{ _('Email:') }} {{ email }}
  • {% endif %} {% trans %} (you'll need these to sign in to the mobile and desktop apps) diff --git a/templates/zerver/emails/followup_day1.txt b/templates/zerver/emails/followup_day1.txt index a0b3109682..3394e5c3fb 100644 --- a/templates/zerver/emails/followup_day1.txt +++ b/templates/zerver/emails/followup_day1.txt @@ -12,8 +12,12 @@ You've joined the Zulip organization {{ realm_name }}. {{ _('Your account details:') }} * {{ _('Organization URL:') }} {{ realm_uri }} +{% if ldap %} {% if ldap_username %} -* {{ _('LDAP username:') }} {{ ldap_username }} +* {{ _('Username:') }} {{ ldap_username }} +{% else %} +* {{ _('Use your LDAP account to login') }} +{% endif %} {% else %} * {{ _('Email:') }} {{ email }} {% endif %} diff --git a/zerver/lib/notifications.py b/zerver/lib/notifications.py index 7baf19e025..178635cfbf 100644 --- a/zerver/lib/notifications.py +++ b/zerver/lib/notifications.py @@ -523,8 +523,15 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool=False) -> Non context['getting_started_link'] = "https://zulipchat.com" from zproject.backends import email_belongs_to_ldap, require_email_format_usernames - if email_belongs_to_ldap(user.realm, user.email) and not require_email_format_usernames(user.realm): - context["ldap_username"] = True + + if email_belongs_to_ldap(user.realm, user.email): + context["ldap"] = True + if settings.LDAP_APPEND_DOMAIN: + for backend in get_backends(): + if isinstance(backend, LDAPBackend): + context["ldap_username"] = backend.django_to_ldap_username(user.email) + elif not settings.LDAP_EMAIL_ATTR: + context["ldap_username"] = user.email send_future_email( "zerver/emails/followup_day1", user.realm, to_user_id=user.id, from_name=from_name, diff --git a/zerver/tests/test_notifications.py b/zerver/tests/test_notifications.py index aeb05a1559..d2206f6149 100644 --- a/zerver/tests/test_notifications.py +++ b/zerver/tests/test_notifications.py @@ -3,11 +3,13 @@ import os import random import re import ujson +import ldap from django.conf import settings from django.core import mail from django.http import HttpResponse from django.test import override_settings +from django_auth_ldap.config import LDAPSearch from email.utils import formataddr from mock import patch, MagicMock from typing import Any, Dict, List, Optional @@ -52,12 +54,43 @@ class TestFollowupEmails(ZulipTestCase): "http://zulip.testserver/help/getting-your-organization-started-with-zulip") self.assertNotIn("ldap_username", email_data["context"]) + # See https://zulip.readthedocs.io/en/latest/production/authentication-methods.html#ldap-including-active-directory + # for case details. @override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipLDAPAuthBackend', 'zproject.backends.ZulipDummyBackend')) - def test_day1_email_ldap_login_credentials(self) -> None: - password = "testing" - email = "newuser@zulip.com" - + def test_day1_email_ldap_case_a_login_credentials(self) -> None: + ldap_user_attr_map = {'full_name': 'fn', 'short_name': 'sn'} + + ldap_patcher = patch('django_auth_ldap.config.ldap.initialize') + mock_initialize = ldap_patcher.start() + mock_ldap = MockLDAP() + mock_initialize.return_value = mock_ldap + + mock_ldap.directory = { + "uid=newuser@zulip.com,ou=users,dc=zulip,dc=com": { + 'userPassword': 'testing', + 'fn': ['full_name'], + 'sn': ['shortname'], + } + } + + ldap_search = LDAPSearch("ou=users,dc=zulip,dc=com", ldap.SCOPE_SUBTREE, "(email=%(user)s)") + with self.settings( + AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map, + AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=users,dc=zulip,dc=com', + AUTH_LDAP_USER_SEARCH=ldap_search): + self.login_with_return("newuser@zulip.com", "testing") + user = UserProfile.objects.get(email="newuser@zulip.com") + scheduled_emails = ScheduledEmail.objects.filter(user=user) + + self.assertEqual(len(scheduled_emails), 2) + email_data = ujson.loads(scheduled_emails[0].data) + self.assertEqual(email_data["context"]["ldap"], True) + self.assertEqual(email_data["context"]["ldap_username"], "newuser@zulip.com") + + @override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipLDAPAuthBackend', + 'zproject.backends.ZulipDummyBackend')) + def test_day1_email_ldap_case_b_login_credentials(self) -> None: ldap_user_attr_map = {'full_name': 'fn', 'short_name': 'sn'} ldap_patcher = patch('django_auth_ldap.config.ldap.initialize') @@ -65,11 +98,10 @@ class TestFollowupEmails(ZulipTestCase): mock_ldap = MockLDAP() mock_initialize.return_value = mock_ldap - full_name = 'New LDAP fullname' mock_ldap.directory = { 'uid=newuser,ou=users,dc=zulip,dc=com': { 'userPassword': 'testing', - 'fn': [full_name], + 'fn': ['full_name'], 'sn': ['shortname'], } } @@ -78,14 +110,48 @@ class TestFollowupEmails(ZulipTestCase): LDAP_APPEND_DOMAIN='zulip.com', AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map, AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=users,dc=zulip,dc=com'): - self.login_with_return(email, password) - user = UserProfile.objects.get(email=email) + self.login_with_return("newuser@zulip.com", "testing") + + user = UserProfile.objects.get(email="newuser@zulip.com") scheduled_emails = ScheduledEmail.objects.filter(user=user) self.assertEqual(len(scheduled_emails), 2) email_data = ujson.loads(scheduled_emails[0].data) - self.assertEqual(email_data["context"]["ldap_username"], True) + self.assertEqual(email_data["context"]["ldap"], True) + self.assertEqual(email_data["context"]["ldap_username"], "newuser") + + @override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipLDAPAuthBackend', + 'zproject.backends.ZulipDummyBackend')) + def test_day1_email_ldap_case_c_login_credentials(self) -> None: + ldap_user_attr_map = {'full_name': 'fn', 'short_name': 'sn'} + + ldap_patcher = patch('django_auth_ldap.config.ldap.initialize') + mock_initialize = ldap_patcher.start() + mock_ldap = MockLDAP() + mock_initialize.return_value = mock_ldap + + mock_ldap.directory = { + 'uid=newuser,ou=users,dc=zulip,dc=com': { + 'userPassword': 'testing', + 'fn': ['full_name'], + 'sn': ['shortname'], + 'email': ['newuser_email@zulip.com'], + } + } + + with self.settings( + LDAP_EMAIL_ATTR='email', + AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map, + AUTH_LDAP_USER_DN_TEMPLATE='uid=%(user)s,ou=users,dc=zulip,dc=com'): + self.login_with_return("newuser", "testing") + user = UserProfile.objects.get(email="newuser_email@zulip.com") + scheduled_emails = ScheduledEmail.objects.filter(user=user) + + self.assertEqual(len(scheduled_emails), 2) + email_data = ujson.loads(scheduled_emails[0].data) + self.assertEqual(email_data["context"]["ldap"], True) + self.assertNotIn("ldap_username", email_data["context"]) def test_followup_emails_count(self) -> None: hamlet = self.example_user("hamlet")