forms.py 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199
  1. import sys
  2. from django import forms
  3. from django.conf import settings
  4. from django.contrib import messages
  5. from django.contrib.auth import forms as auth_forms, hashers, tokens
  6. from django.contrib.sites import models as sites_models
  7. from django.template import loader
  8. from django.utils import http
  9. from django.utils.translation import ugettext_lazy as _
  10. import bson
  11. from . import backends, models
  12. class UserUsernameForm(forms.Form):
  13. """
  14. Class with username form.
  15. """
  16. username = forms.RegexField(
  17. label=_("Username"),
  18. max_length=30,
  19. min_length=4,
  20. regex=r'^' + models.USERNAME_REGEX + r'$',
  21. help_text=_("Minimal of 4 characters and maximum of 30. Letters, digits and @/./+/-/_ only."),
  22. error_messages={
  23. 'invalid': _("This value may contain only letters, numbers and @/./+/-/_ characters."),
  24. }
  25. )
  26. def clean_username(self):
  27. """
  28. This method checks whether the username exists in a case-insensitive manner.
  29. """
  30. username = self.cleaned_data['username']
  31. if backends.User.objects(username__iexact=username).count():
  32. raise forms.ValidationError(_("A user with that username already exists."), code='username_exists')
  33. return username
  34. class UserPasswordForm(forms.Form):
  35. """
  36. Class with user password form.
  37. """
  38. password1 = forms.CharField(
  39. label=_("Password"),
  40. min_length=6,
  41. widget=forms.PasswordInput,
  42. )
  43. password2 = forms.CharField(
  44. label=_("Password (repeat)"),
  45. widget=forms.PasswordInput,
  46. help_text=_("Enter the same password as above, for verification."),
  47. )
  48. def clean_password2(self):
  49. """
  50. This method checks whether the passwords match.
  51. """
  52. password1 = self.cleaned_data.get('password1')
  53. password2 = self.cleaned_data.get('password2')
  54. if password1 and password1 != password2:
  55. raise forms.ValidationError(_("The two password fields did not match."), code='password_mismatch')
  56. return password2
  57. class UserCurrentPasswordForm(forms.Form):
  58. """
  59. Class with user current password form.
  60. """
  61. current_password = forms.CharField(
  62. label=_("Current password"),
  63. widget=forms.PasswordInput,
  64. help_text=_("Enter your current password, for verification."),
  65. )
  66. def __init__(self, user, *args, **kwargs):
  67. self.user = user
  68. super(UserCurrentPasswordForm, self).__init__(*args, **kwargs)
  69. def clean_current_password(self):
  70. """
  71. This method checks if user password is correct.
  72. """
  73. password = self.cleaned_data['current_password']
  74. if not self.user.check_password(password):
  75. raise forms.ValidationError(_("Your current password was incorrect."), code='password_incorrect')
  76. return password
  77. class UserBasicInfoForm(forms.Form):
  78. """
  79. Class with user basic information form.
  80. """
  81. first_name = forms.CharField(label=_("First name"))
  82. last_name = forms.CharField(label=_("Last name"))
  83. email = forms.EmailField(label=_("E-mail"))
  84. class RegistrationForm(UserUsernameForm, UserPasswordForm, UserBasicInfoForm):
  85. """
  86. Class with registration form.
  87. """
  88. class AccountChangeForm(UserBasicInfoForm, UserCurrentPasswordForm):
  89. """
  90. Class with form for changing your account settings.
  91. """
  92. class PasswordChangeForm(UserCurrentPasswordForm, UserPasswordForm):
  93. """
  94. Class with form for changing password.
  95. """
  96. class EmailConfirmationSendTokenForm(forms.Form):
  97. """
  98. Form for sending an e-mail address confirmation token.
  99. """
  100. class EmailConfirmationProcessTokenForm(forms.Form):
  101. """
  102. Form for processing an e-mail address confirmation token.
  103. """
  104. confirmation_token = forms.CharField(
  105. label=_("Confirmation token"),
  106. min_length=20,
  107. max_length=20,
  108. required=True,
  109. help_text=_("Please enter the confirmation token you received to your e-mail address."),
  110. )
  111. def __init__(self, user, *args, **kwargs):
  112. self.user = user
  113. super(EmailConfirmationProcessTokenForm, self).__init__(*args, **kwargs)
  114. def clean_confirmation_token(self):
  115. """
  116. This method checks if user confirmation token is correct.
  117. """
  118. confirmation_token = self.cleaned_data['confirmation_token']
  119. if not self.user.email_confirmation_token.check_token(confirmation_token):
  120. raise forms.ValidationError(_("The confirmation token is invalid or has expired. Please retry."), code='confirmation_token_incorrect')
  121. return confirmation_token
  122. def objectid_to_base36(i):
  123. assert isinstance(i, bson.ObjectId), type(i)
  124. old_maxint = sys.maxint
  125. sys.maxint = old_maxint**2
  126. try:
  127. return http.int_to_base36(int(str(i), 16))
  128. finally:
  129. sys.maxint = old_maxint
  130. class PasswordResetForm(auth_forms.PasswordResetForm):
  131. def clean_email(self):
  132. email = self.cleaned_data["email"]
  133. self.users_cache = backends.User.objects.filter(email__iexact=email, is_active=True)
  134. if not len(self.users_cache):
  135. raise forms.ValidationError(self.error_messages['unknown'])
  136. # We use "all" instead of "any" and skip all users with unusable passwords in save
  137. if all((user.password == hashers.UNUSABLE_PASSWORD) for user in self.users_cache):
  138. raise forms.ValidationError(self.error_messages['unusable'])
  139. return email
  140. def save(self, domain_override=None, subject_template_name='mongo_auth/password_reset_subject.txt', email_template_name='mongo_auth/password_reset_email.html', use_https=False, token_generator=tokens.default_token_generator, from_email=None, request=None):
  141. for user in self.users_cache:
  142. if user.password == hashers.UNUSABLE_PASSWORD:
  143. continue
  144. if not domain_override:
  145. current_site = sites_models.get_current_site(request)
  146. site_name = current_site.name
  147. domain = current_site.domain
  148. else:
  149. site_name = domain = domain_override
  150. c = {
  151. 'PASSWORD_RESET_TIMEOUT_DAYS': settings.PASSWORD_RESET_TIMEOUT_DAYS,
  152. 'EMAIL_SUBJECT_PREFIX': settings.EMAIL_SUBJECT_PREFIX,
  153. 'SITE_NAME': getattr(settings, 'SITE_NAME', None),
  154. 'email': user.email,
  155. 'email_address': user.email,
  156. 'request': request,
  157. 'domain': domain,
  158. 'site_name': site_name,
  159. 'uid': objectid_to_base36(user.id),
  160. 'user': user,
  161. 'token': token_generator.make_token(user),
  162. 'protocol': use_https and 'https' or 'http',
  163. }
  164. subject = loader.render_to_string(subject_template_name, c)
  165. # Email subject *must not* contain newlines
  166. subject = ''.join(subject.splitlines())
  167. email = loader.render_to_string(email_template_name, c)
  168. user.email_user(subject, email, from_email)
  169. messages.success(request, _("We've e-mailed you instructions for setting your password to the e-mail address you submitted. You should be receiving it shortly."))