mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 14:03:30 +00:00 
			
		
		
		
	emails: Cover all LDAP cases when emailing login details.
This provides a nice user experience for folks where we do know what their LDAP credentials are. Though we need to fix #10917 before the content in the email with be correct.
This commit is contained in:
		@@ -22,10 +22,14 @@
 | 
				
			|||||||
<p>
 | 
					<p>
 | 
				
			||||||
    {{ _('Your account details:') }}
 | 
					    {{ _('Your account details:') }}
 | 
				
			||||||
    <li>{{ _('Organization URL:') }} {{ realm_uri }}<br/></li>
 | 
					    <li>{{ _('Organization URL:') }} {{ realm_uri }}<br/></li>
 | 
				
			||||||
    {% if ldap_username %}
 | 
					    {% if ldap %}
 | 
				
			||||||
    <li>{{ _('Use your LDAP account to login') }}<br/></li>
 | 
					        {% if ldap_username %}
 | 
				
			||||||
 | 
					        <li>{{ _('Username:') }} {{ ldap_username }}<br/></li>
 | 
				
			||||||
 | 
					        {% else %}
 | 
				
			||||||
 | 
					        <li>{{ _('Use your LDAP account to login') }}<br/></li>
 | 
				
			||||||
 | 
					        {% endif %}
 | 
				
			||||||
    {% else %}
 | 
					    {% else %}
 | 
				
			||||||
    <li>{{ _('Email:') }} {{ email }}<br/></li>
 | 
					        <li>{{ _('Email:') }} {{ email }}<br/></li>
 | 
				
			||||||
    {% endif %}
 | 
					    {% endif %}
 | 
				
			||||||
    {% trans %}
 | 
					    {% trans %}
 | 
				
			||||||
    (you'll need these to sign in to the <a href="https://zulipchat.com/apps">mobile and desktop</a> apps)
 | 
					    (you'll need these to sign in to the <a href="https://zulipchat.com/apps">mobile and desktop</a> apps)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -12,8 +12,12 @@ You've joined the Zulip organization {{ realm_name }}.
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
{{ _('Your account details:') }}
 | 
					{{ _('Your account details:') }}
 | 
				
			||||||
* {{ _('Organization URL:') }} {{ realm_uri }}
 | 
					* {{ _('Organization URL:') }} {{ realm_uri }}
 | 
				
			||||||
 | 
					{% if ldap %}
 | 
				
			||||||
{% if ldap_username %}
 | 
					{% if ldap_username %}
 | 
				
			||||||
* {{ _('LDAP username:') }} {{ ldap_username }}
 | 
					* {{ _('Username:') }} {{ ldap_username }}
 | 
				
			||||||
 | 
					{% else %}
 | 
				
			||||||
 | 
					* {{ _('Use your LDAP account to login') }}
 | 
				
			||||||
 | 
					{% endif %}
 | 
				
			||||||
{% else %}
 | 
					{% else %}
 | 
				
			||||||
* {{ _('Email:') }} {{ email }}
 | 
					* {{ _('Email:') }} {{ email }}
 | 
				
			||||||
{% endif %}
 | 
					{% endif %}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -523,8 +523,15 @@ def enqueue_welcome_emails(user: UserProfile, realm_creation: bool=False) -> Non
 | 
				
			|||||||
        context['getting_started_link'] = "https://zulipchat.com"
 | 
					        context['getting_started_link'] = "https://zulipchat.com"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    from zproject.backends import email_belongs_to_ldap, require_email_format_usernames
 | 
					    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(
 | 
					    send_future_email(
 | 
				
			||||||
        "zerver/emails/followup_day1", user.realm, to_user_id=user.id, from_name=from_name,
 | 
					        "zerver/emails/followup_day1", user.realm, to_user_id=user.id, from_name=from_name,
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,11 +3,13 @@ import os
 | 
				
			|||||||
import random
 | 
					import random
 | 
				
			||||||
import re
 | 
					import re
 | 
				
			||||||
import ujson
 | 
					import ujson
 | 
				
			||||||
 | 
					import ldap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
from django.core import mail
 | 
					from django.core import mail
 | 
				
			||||||
from django.http import HttpResponse
 | 
					from django.http import HttpResponse
 | 
				
			||||||
from django.test import override_settings
 | 
					from django.test import override_settings
 | 
				
			||||||
 | 
					from django_auth_ldap.config import LDAPSearch
 | 
				
			||||||
from email.utils import formataddr
 | 
					from email.utils import formataddr
 | 
				
			||||||
from mock import patch, MagicMock
 | 
					from mock import patch, MagicMock
 | 
				
			||||||
from typing import Any, Dict, List, Optional
 | 
					from typing import Any, Dict, List, Optional
 | 
				
			||||||
@@ -52,12 +54,43 @@ class TestFollowupEmails(ZulipTestCase):
 | 
				
			|||||||
                         "http://zulip.testserver/help/getting-your-organization-started-with-zulip")
 | 
					                         "http://zulip.testserver/help/getting-your-organization-started-with-zulip")
 | 
				
			||||||
        self.assertNotIn("ldap_username", email_data["context"])
 | 
					        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',
 | 
					    @override_settings(AUTHENTICATION_BACKENDS=('zproject.backends.ZulipLDAPAuthBackend',
 | 
				
			||||||
                                                'zproject.backends.ZulipDummyBackend'))
 | 
					                                                'zproject.backends.ZulipDummyBackend'))
 | 
				
			||||||
    def test_day1_email_ldap_login_credentials(self) -> None:
 | 
					    def test_day1_email_ldap_case_a_login_credentials(self) -> None:
 | 
				
			||||||
        password = "testing"
 | 
					        ldap_user_attr_map = {'full_name': 'fn', 'short_name': 'sn'}
 | 
				
			||||||
        email = "newuser@zulip.com"
 | 
					
 | 
				
			||||||
 | 
					        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_user_attr_map = {'full_name': 'fn', 'short_name': 'sn'}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        ldap_patcher = patch('django_auth_ldap.config.ldap.initialize')
 | 
					        ldap_patcher = patch('django_auth_ldap.config.ldap.initialize')
 | 
				
			||||||
@@ -65,11 +98,10 @@ class TestFollowupEmails(ZulipTestCase):
 | 
				
			|||||||
        mock_ldap = MockLDAP()
 | 
					        mock_ldap = MockLDAP()
 | 
				
			||||||
        mock_initialize.return_value = mock_ldap
 | 
					        mock_initialize.return_value = mock_ldap
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        full_name = 'New LDAP fullname'
 | 
					 | 
				
			||||||
        mock_ldap.directory = {
 | 
					        mock_ldap.directory = {
 | 
				
			||||||
            'uid=newuser,ou=users,dc=zulip,dc=com': {
 | 
					            'uid=newuser,ou=users,dc=zulip,dc=com': {
 | 
				
			||||||
                'userPassword': 'testing',
 | 
					                'userPassword': 'testing',
 | 
				
			||||||
                'fn': [full_name],
 | 
					                'fn': ['full_name'],
 | 
				
			||||||
                'sn': ['shortname'],
 | 
					                'sn': ['shortname'],
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -78,14 +110,48 @@ class TestFollowupEmails(ZulipTestCase):
 | 
				
			|||||||
                LDAP_APPEND_DOMAIN='zulip.com',
 | 
					                LDAP_APPEND_DOMAIN='zulip.com',
 | 
				
			||||||
                AUTH_LDAP_USER_ATTR_MAP=ldap_user_attr_map,
 | 
					                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_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)
 | 
					            scheduled_emails = ScheduledEmail.objects.filter(user=user)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            self.assertEqual(len(scheduled_emails), 2)
 | 
					            self.assertEqual(len(scheduled_emails), 2)
 | 
				
			||||||
            email_data = ujson.loads(scheduled_emails[0].data)
 | 
					            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:
 | 
					    def test_followup_emails_count(self) -> None:
 | 
				
			||||||
        hamlet = self.example_user("hamlet")
 | 
					        hamlet = self.example_user("hamlet")
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user