mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-04 05:53:43 +00:00 
			
		
		
		
	No change to behavior. non_mit_mailing_list never returned False, so it was never possible to reach the line "Otherwise, the user is an MIT mailing list, and .."
		
			
				
	
	
		
			235 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			235 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
from __future__ import absolute_import
 | 
						|
 | 
						|
from django import forms
 | 
						|
from django.conf import settings
 | 
						|
from django.contrib.auth.forms import SetPasswordForm, AuthenticationForm, \
 | 
						|
    PasswordResetForm
 | 
						|
from django.core.exceptions import ValidationError
 | 
						|
from django.core.urlresolvers import reverse
 | 
						|
from django.db.models.query import QuerySet
 | 
						|
from django.utils.translation import ugettext as _
 | 
						|
from jinja2 import Markup as mark_safe
 | 
						|
 | 
						|
from zerver.lib.actions import do_change_password, is_inactive, user_email_is_unique
 | 
						|
from zerver.lib.name_restrictions import is_reserved_subdomain, is_disposable_domain
 | 
						|
from zerver.lib.utils import get_subdomain, check_subdomain
 | 
						|
from zerver.models import Realm, get_user_profile_by_email, UserProfile, \
 | 
						|
    completely_open, resolve_email_to_domain, get_realm, get_realm_by_string_id, \
 | 
						|
    get_unique_open_realm, split_email_to_domain
 | 
						|
from zproject.backends import password_auth_enabled
 | 
						|
 | 
						|
import logging
 | 
						|
import re
 | 
						|
import DNS
 | 
						|
 | 
						|
from six import text_type
 | 
						|
from typing import Any, Callable, Optional
 | 
						|
 | 
						|
SIGNUP_STRING = u'Your e-mail does not match any existing open organization. ' + \
 | 
						|
                u'Use a different e-mail address, or contact %s with questions.' % (settings.ZULIP_ADMINISTRATOR,)
 | 
						|
 | 
						|
if settings.SHOW_OSS_ANNOUNCEMENT:
 | 
						|
    SIGNUP_STRING = u'Your e-mail does not match any existing organization. <br />' + \
 | 
						|
                    u"The zulip.com service is not taking new customer teams. <br /> " + \
 | 
						|
                    u"<a href=\"https://blogs.dropbox.com/tech/2015/09/open-sourcing-zulip-a-dropbox-hack-week-project/\">" + \
 | 
						|
                    u"Zulip is open source</a>, so you can install your own Zulip server " + \
 | 
						|
                    u"by following the instructions on <a href=\"https://www.zulip.org\">www.zulip.org</a>!"
 | 
						|
MIT_VALIDATION_ERROR = u'That user does not exist at MIT or is a ' + \
 | 
						|
                       u'<a href="https://ist.mit.edu/email-lists">mailing list</a>. ' + \
 | 
						|
                       u'If you want to sign up an alias for Zulip, ' + \
 | 
						|
                       u'<a href="mailto:support@zulipchat.com">contact us</a>.'
 | 
						|
WRONG_SUBDOMAIN_ERROR = "Your Zulip account is not a member of the " + \
 | 
						|
                        "organization associated with this subdomain.  " + \
 | 
						|
                        "Please contact %s with any questions!" % (settings.ZULIP_ADMINISTRATOR,)
 | 
						|
 | 
						|
def get_registration_string(domain):
 | 
						|
    # type: (text_type) -> text_type
 | 
						|
    register_url  = reverse('register') + domain
 | 
						|
    register_account_string = _('The organization with the domain already exists. '
 | 
						|
                                'Please register your account <a href=%(url)s>here</a>.') % {'url': register_url}
 | 
						|
    return register_account_string
 | 
						|
 | 
						|
def get_valid_realm(email):
 | 
						|
    # type: (str) -> Optional[Realm]
 | 
						|
    """Checks if there is a realm without invite_required
 | 
						|
    matching the domain of the input e-mail."""
 | 
						|
    realm = get_realm(resolve_email_to_domain(email))
 | 
						|
    if realm is None or realm.invite_required:
 | 
						|
        return None
 | 
						|
    return realm
 | 
						|
 | 
						|
def email_is_not_mit_mailing_list(email):
 | 
						|
    # type: (text_type) -> None
 | 
						|
    """Prevent MIT mailing lists from signing up for Zulip"""
 | 
						|
    if "@mit.edu" in email:
 | 
						|
        username = email.rsplit("@", 1)[0]
 | 
						|
        # Check whether the user exists and can get mail.
 | 
						|
        try:
 | 
						|
            DNS.dnslookup("%s.pobox.ns.athena.mit.edu" % username, DNS.Type.TXT)
 | 
						|
        except DNS.Base.ServerError as e:
 | 
						|
            if e.rcode == DNS.Status.NXDOMAIN:
 | 
						|
                raise ValidationError(mark_safe(MIT_VALIDATION_ERROR))
 | 
						|
            else:
 | 
						|
                raise
 | 
						|
 | 
						|
class RegistrationForm(forms.Form):
 | 
						|
    full_name = forms.CharField(max_length=100)
 | 
						|
    # The required-ness of the password field gets overridden if it isn't
 | 
						|
    # actually required for a realm
 | 
						|
    password = forms.CharField(widget=forms.PasswordInput, max_length=100,
 | 
						|
                               required=False)
 | 
						|
    realm_name = forms.CharField(max_length=100, required=False)
 | 
						|
    realm_subdomain = forms.CharField(max_length=40, required=False)
 | 
						|
    realm_org_type = forms.ChoiceField(((Realm.COMMUNITY, 'Community'),
 | 
						|
                                        (Realm.CORPORATE, 'Corporate')), \
 | 
						|
                                       initial=Realm.COMMUNITY, required=False)
 | 
						|
 | 
						|
    if settings.TERMS_OF_SERVICE:
 | 
						|
        terms = forms.BooleanField(required=True)
 | 
						|
 | 
						|
    def clean_realm_subdomain(self):
 | 
						|
        # type: () -> str
 | 
						|
        if settings.REALMS_HAVE_SUBDOMAINS:
 | 
						|
            error_strings = {
 | 
						|
                'too short': _("Subdomain needs to have length 3 or greater."),
 | 
						|
                'extremal dash': _("Subdomain cannot start or end with a '-'."),
 | 
						|
                'bad character': _("Subdomain can only have lowercase letters, numbers, and '-'s."),
 | 
						|
                'unavailable': _("Subdomain unavailable. Please choose a different one.")}
 | 
						|
        else:
 | 
						|
            error_strings = {
 | 
						|
                'too short': _("Short name needs at least 3 characters."),
 | 
						|
                'extremal dash': _("Short name cannot start or end with a '-'."),
 | 
						|
                'bad character': _("Short name can only have lowercase letters, numbers, and '-'s."),
 | 
						|
                'unavailable': _("Short name unavailable. Please choose a different one.")}
 | 
						|
        subdomain = self.cleaned_data['realm_subdomain']
 | 
						|
        if not subdomain:
 | 
						|
            return ''
 | 
						|
        if len(subdomain) < 3:
 | 
						|
            raise ValidationError(error_strings['too short'])
 | 
						|
        if subdomain[0] == '-' or subdomain[-1] == '-':
 | 
						|
            raise ValidationError(error_strings['extremal dash'])
 | 
						|
        if not re.match('^[a-z0-9-]*$', subdomain):
 | 
						|
            raise ValidationError(error_strings['bad character'])
 | 
						|
        if is_reserved_subdomain(subdomain) or \
 | 
						|
           get_realm_by_string_id(subdomain) is not None:
 | 
						|
            raise ValidationError(error_strings['unavailable'])
 | 
						|
        return subdomain
 | 
						|
 | 
						|
class ToSForm(forms.Form):
 | 
						|
    terms = forms.BooleanField(required=True)
 | 
						|
 | 
						|
class HomepageForm(forms.Form):
 | 
						|
    # This form is important because it determines whether users can
 | 
						|
    # register for our product. Be careful when modifying the
 | 
						|
    # validators.
 | 
						|
    email = forms.EmailField(validators=[is_inactive,])
 | 
						|
 | 
						|
    def __init__(self, *args, **kwargs):
 | 
						|
        # type: (*Any, **Any) -> None
 | 
						|
        self.domain = kwargs.get("domain")
 | 
						|
        self.subdomain = kwargs.get("subdomain")
 | 
						|
        if "domain" in kwargs:
 | 
						|
            del kwargs["domain"]
 | 
						|
        if "subdomain" in kwargs:
 | 
						|
            del kwargs["subdomain"]
 | 
						|
        super(HomepageForm, self).__init__(*args, **kwargs)
 | 
						|
 | 
						|
    def clean_email(self):
 | 
						|
        # type: () -> str
 | 
						|
        """Returns the email if and only if the user's email address is
 | 
						|
        allowed to join the realm they are trying to join."""
 | 
						|
        email = self.cleaned_data['email']
 | 
						|
        # If the server has a unique open realm, pass
 | 
						|
        if get_unique_open_realm():
 | 
						|
            return email
 | 
						|
 | 
						|
        # If a realm is specified and that realm is open, pass
 | 
						|
        if completely_open(self.domain):
 | 
						|
            return email
 | 
						|
 | 
						|
        # If the subdomain encodes a complete open realm, pass
 | 
						|
        subdomain_realm = get_realm_by_string_id(self.subdomain)
 | 
						|
        if (subdomain_realm is not None and
 | 
						|
            completely_open(subdomain_realm.domain)):
 | 
						|
            return email
 | 
						|
 | 
						|
        # If no realm is specified, fail
 | 
						|
        realm = get_valid_realm(email)
 | 
						|
        if realm is None:
 | 
						|
            raise ValidationError(mark_safe(SIGNUP_STRING))
 | 
						|
 | 
						|
        if realm.is_zephyr_mirror_realm:
 | 
						|
            email_is_not_mit_mailing_list(email)
 | 
						|
 | 
						|
        return email
 | 
						|
 | 
						|
def email_is_not_disposable(email):
 | 
						|
    # type: (text_type) -> None
 | 
						|
    if is_disposable_domain(split_email_to_domain(email)):
 | 
						|
        raise ValidationError(_("Please use your real email address."))
 | 
						|
 | 
						|
class RealmCreationForm(forms.Form):
 | 
						|
    # This form determines whether users can
 | 
						|
    # create a new realm. Be careful when modifying the
 | 
						|
    # validators.
 | 
						|
    email = forms.EmailField(validators=[user_email_is_unique, email_is_not_disposable])
 | 
						|
 | 
						|
    def __init__(self, *args, **kwargs):
 | 
						|
        # type: (*Any, **Any) -> None
 | 
						|
        self.domain = kwargs.get("domain")
 | 
						|
        if "domain" in kwargs:
 | 
						|
            del kwargs["domain"]
 | 
						|
        super(RealmCreationForm, self).__init__(*args, **kwargs)
 | 
						|
 | 
						|
class LoggingSetPasswordForm(SetPasswordForm):
 | 
						|
    def save(self, commit=True):
 | 
						|
        # type: (bool) -> UserProfile
 | 
						|
        do_change_password(self.user, self.cleaned_data['new_password1'],
 | 
						|
                           log=True, commit=commit)
 | 
						|
        return self.user
 | 
						|
 | 
						|
class ZulipPasswordResetForm(PasswordResetForm):
 | 
						|
    def get_users(self, email):
 | 
						|
        # type: (str) -> QuerySet
 | 
						|
        """Given an email, return matching user(s) who should receive a reset.
 | 
						|
 | 
						|
        This is modified from the original in that it allows non-bot
 | 
						|
        users who don't have a usable password to reset their
 | 
						|
        passwords.
 | 
						|
        """
 | 
						|
        if not password_auth_enabled:
 | 
						|
            logging.info("Password reset attempted for %s even though password auth is disabled." % (email,))
 | 
						|
            return []
 | 
						|
        result = UserProfile.objects.filter(email__iexact=email, is_active=True,
 | 
						|
                                            is_bot=False)
 | 
						|
        if len(result) == 0:
 | 
						|
            logging.info("Password reset attempted for %s; no active account." % (email,))
 | 
						|
        return result
 | 
						|
 | 
						|
class CreateUserForm(forms.Form):
 | 
						|
    full_name = forms.CharField(max_length=100)
 | 
						|
    email = forms.EmailField()
 | 
						|
 | 
						|
class OurAuthenticationForm(AuthenticationForm):
 | 
						|
    def clean_username(self):
 | 
						|
        # type: () -> str
 | 
						|
        email = self.cleaned_data['username']
 | 
						|
        try:
 | 
						|
            user_profile = get_user_profile_by_email(email)
 | 
						|
        except UserProfile.DoesNotExist:
 | 
						|
            return email
 | 
						|
 | 
						|
        if user_profile.realm.deactivated:
 | 
						|
            error_msg = u"""Sorry for the trouble, but %s has been deactivated.
 | 
						|
 | 
						|
Please contact %s to reactivate this group.""" % (
 | 
						|
                user_profile.realm.name,
 | 
						|
                settings.ZULIP_ADMINISTRATOR)
 | 
						|
            raise ValidationError(mark_safe(error_msg))
 | 
						|
 | 
						|
        if not check_subdomain(get_subdomain(self.request), user_profile.realm.subdomain):
 | 
						|
            logging.warning("User %s attempted to password login to wrong subdomain %s" %
 | 
						|
                            (user_profile.email, get_subdomain(self.request)))
 | 
						|
            raise ValidationError(mark_safe(WRONG_SUBDOMAIN_ERROR))
 | 
						|
        return email
 |