2013-04-23 18:51:17 +02:00
from __future__ import absolute_import
2016-06-05 20:47:49 +02:00
from typing import Any , Callable , Optional
2013-04-23 18:51:17 +02:00
2012-08-28 18:44:51 +02:00
from django import forms
2012-09-26 20:08:39 +02:00
from django . core . exceptions import ValidationError
2016-04-28 23:07:41 +02:00
from django . contrib . auth . forms import SetPasswordForm , AuthenticationForm , \
PasswordResetForm
2013-11-13 23:23:37 +01:00
from django . conf import settings
2016-06-05 20:47:49 +02:00
from django . db . models . query import QuerySet
2016-04-21 08:48:33 +02:00
from jinja2 import Markup as mark_safe
2016-06-03 01:02:58 +02:00
from django . core . urlresolvers import reverse
from django . utils . translation import ugettext as _
2012-11-21 21:14:55 +01:00
2016-04-28 23:13:59 +02:00
import logging
2013-08-07 17:59:45 +02:00
from zerver . models import Realm , get_user_profile_by_email , UserProfile , \
2016-06-03 01:02:58 +02:00
completely_open , resolve_email_to_domain , get_realm , \
get_unique_open_realm , split_email_to_domain
from zerver . lib . actions import do_change_password , is_inactive , user_email_is_unique
2013-11-13 23:23:59 +01:00
from zproject . backends import password_auth_enabled
2013-08-12 00:47:28 +02:00
import DNS
2016-06-03 01:02:58 +02:00
from six import text_type
2012-09-26 20:08:39 +02:00
2015-10-17 18:43:27 +02:00
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 . ZULIP_COM :
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 /> " + \
2016-06-23 23:34:37 +02:00
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 " + \
2015-10-17 18:43:27 +02:00
u " by following the instructions on <a href= \" https://www.zulip.org \" >www.zulip.org</a>! "
2016-06-23 23:34:37 +02:00
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@zulip.com " >contact us</a>. '
2016-06-03 01:02:58 +02:00
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
2012-11-21 21:14:55 +01:00
def has_valid_realm ( value ) :
2016-06-05 20:47:49 +02:00
# type: (str) -> bool
2015-08-20 02:38:32 +02:00
# Checks if there is a realm without invite_required
# matching the domain of the input e-mail.
2015-10-17 18:41:06 +02:00
realm = get_realm ( resolve_email_to_domain ( value ) )
return realm is not None and not realm . invite_required
2012-11-21 21:14:55 +01:00
2013-08-12 00:47:28 +02:00
def not_mit_mailing_list ( value ) :
2016-06-05 20:47:49 +02:00
# type: (str) -> bool
2013-08-12 00:47:28 +02:00
# I don't want ec-discuss signed up for Zulip
if " @mit.edu " in value :
username = value . 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 )
2013-08-12 00:55:24 +02:00
return True
2015-11-01 17:08:33 +01:00
except DNS . Base . ServerError as e :
2013-08-12 00:47:28 +02:00
if e . rcode == DNS . Status . NXDOMAIN :
2016-06-23 23:34:37 +02:00
raise ValidationError ( mark_safe ( MIT_VALIDATION_ERROR ) )
2013-08-12 00:47:28 +02:00
else :
raise
2013-08-12 00:55:24 +02:00
return True
2013-08-12 00:47:28 +02:00
2012-08-28 18:44:51 +02:00
class RegistrationForm ( forms . Form ) :
2012-09-11 19:20:01 +02:00
full_name = forms . CharField ( max_length = 100 )
2014-03-28 00:45:03 +01:00
# 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 )
2016-06-03 01:02:58 +02:00
realm_name = forms . CharField ( max_length = 100 , required = False )
2015-08-21 11:24:18 +02:00
if not settings . VOYAGER :
2013-11-13 23:23:37 +01:00
terms = forms . BooleanField ( required = True )
2012-09-28 22:47:05 +02:00
2013-01-08 23:26:40 +01:00
class ToSForm ( forms . Form ) :
full_name = forms . CharField ( max_length = 100 )
terms = forms . BooleanField ( required = True )
2012-09-28 22:47:05 +02:00
class HomepageForm ( forms . Form ) :
2013-08-07 17:59:45 +02:00
# This form is important because it determines whether users can
# register for our product. Be careful when modifying the
# validators.
2013-08-12 00:57:54 +02:00
email = forms . EmailField ( validators = [ is_inactive , ] )
2012-12-13 21:08:07 +01:00
2013-08-07 17:59:45 +02:00
def __init__ ( self , * args , * * kwargs ) :
2016-06-05 20:47:49 +02:00
# type: (*Any, **Any) -> None
2013-08-07 17:59:45 +02:00
self . domain = kwargs . get ( " domain " )
2015-11-01 17:10:01 +01:00
if " domain " in kwargs :
2013-08-07 17:59:45 +02:00
del kwargs [ " domain " ]
super ( HomepageForm , self ) . __init__ ( * args , * * kwargs )
def clean_email ( self ) :
2016-06-05 20:47:49 +02:00
# type: () -> str
2013-08-07 17:59:45 +02:00
data = self . cleaned_data [ ' email ' ]
2015-12-28 21:37:24 +01:00
if ( get_unique_open_realm ( ) or
completely_open ( self . domain ) or
( has_valid_realm ( data ) and not_mit_mailing_list ( data ) ) ) :
2013-08-07 17:59:45 +02:00
return data
2015-10-17 18:43:27 +02:00
raise ValidationError ( mark_safe ( SIGNUP_STRING ) )
2013-08-07 17:59:45 +02:00
2016-06-03 01:02:58 +02:00
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 , ] )
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 )
def clean_email ( self ) :
# type: () -> text_type
data = self . cleaned_data [ ' email ' ]
domain = split_email_to_domain ( data )
if ( get_realm ( domain ) is not None ) :
raise ValidationError ( mark_safe ( get_registration_string ( domain ) ) )
return data
2012-12-13 21:08:07 +01:00
class LoggingSetPasswordForm ( SetPasswordForm ) :
def save ( self , commit = True ) :
2016-06-05 20:47:49 +02:00
# type: (bool) -> UserProfile
2013-03-29 17:39:53 +01:00
do_change_password ( self . user , self . cleaned_data [ ' new_password1 ' ] ,
2012-12-13 21:08:07 +01:00
log = True , commit = commit )
return self . user
2013-05-03 00:26:53 +02:00
2016-04-28 23:07:41 +02:00
class ZulipPasswordResetForm ( PasswordResetForm ) :
def get_users ( self , email ) :
2016-06-05 20:47:49 +02:00
# type: (str) -> QuerySet
2016-04-28 23:07:41 +02:00
""" 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 :
2016-04-28 23:13:59 +02:00
logging . info ( " Password reset attempted for %s even though password auth is disabled. " % ( email , ) )
2016-04-28 23:07:41 +02:00
return [ ]
2016-04-28 23:13:59 +02:00
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
2016-04-28 23:07:41 +02:00
2013-12-09 22:26:10 +01:00
class CreateUserForm ( forms . Form ) :
2013-05-03 00:26:53 +02:00
full_name = forms . CharField ( max_length = 100 )
email = forms . EmailField ( )
2014-01-07 19:51:18 +01:00
class OurAuthenticationForm ( AuthenticationForm ) :
def clean_username ( self ) :
2016-06-05 20:47:49 +02:00
# type: () -> str
2014-01-07 19:51:18 +01:00
email = self . cleaned_data [ ' username ' ]
try :
user_profile = get_user_profile_by_email ( email )
except UserProfile . DoesNotExist :
2014-01-09 16:27:01 +01:00
return email
2014-01-07 19:51:18 +01:00
if user_profile . realm . deactivated :
error_msg = u """ Sorry for the trouble, but %s has been deactivated.
2015-09-30 02:58:50 +02:00
Please contact % s to reactivate this group . """ % (
user_profile . realm . name ,
settings . ZULIP_ADMINISTRATOR )
2014-01-07 19:51:18 +01:00
raise ValidationError ( mark_safe ( error_msg ) )
return email