0001-Allow-users-to-provide-their-email-or-username-for-p.patch
src/authentic2/forms/passwords.py | ||
---|---|---|
19 | 19 | |
20 | 20 |
from django.contrib.auth import forms as auth_forms |
21 | 21 |
from django.core.exceptions import ValidationError |
22 |
from django.db.models import Q |
|
22 | 23 |
from django.forms import Form |
23 | 24 |
from django import forms |
24 | 25 |
from django.utils.translation import ugettext_lazy as _ |
... | ... | |
35 | 36 |
class PasswordResetForm(forms.Form): |
36 | 37 |
next_url = forms.CharField(widget=forms.HiddenInput, required=False) |
37 | 38 | |
38 |
email = ValidatedEmailField(
|
|
39 |
label=_("Email"), max_length=254) |
|
39 |
email_or_username = forms.CharField(
|
|
40 |
label=_("Email or username"), max_length=254)
|
|
40 | 41 | |
41 | 42 |
def save(self): |
42 | 43 |
""" |
43 | 44 |
Generates a one-use only link for resetting password and sends to the |
44 | 45 |
user. |
45 | 46 |
""" |
46 |
email = self.cleaned_data["email"].strip()
|
|
47 |
email_or_username = self.cleaned_data["email_or_username"].strip()
|
|
47 | 48 |
users = get_user_queryset() |
48 |
active_users = users.filter(email__iexact=email, is_active=True)
|
|
49 |
active_users = users.filter(Q(email__iexact=email_or_username) | Q(username__iexact=email_or_username), is_active=True)
|
|
49 | 50 |
for user in active_users: |
50 | 51 |
# we don't set the password to a random string, as some users should not have |
51 | 52 |
# a password |
... | ... | |
56 | 57 |
set_random_password=set_random_password, |
57 | 58 |
next_url=self.cleaned_data.get('next_url')) |
58 | 59 |
if not active_users: |
59 |
logger.info(u'password reset request for "%s", no user found', email) |
|
60 |
hooks.call_hooks('event', name='password-reset', email=email, users=active_users)
|
|
60 |
logger.info(u'password reset request for "%s", no user found', email_or_username)
|
|
61 |
hooks.call_hooks('event', name='password-reset', email_or_username=email_or_username, users=active_users)
|
|
61 | 62 | |
62 | 63 | |
63 | 64 |
class PasswordResetMixin(Form): |
src/authentic2/journal_event_types.py | ||
---|---|---|
172 | 172 |
label = _('password reset failure') |
173 | 173 | |
174 | 174 |
@classmethod |
175 |
def record(cls, email): |
|
176 |
super().record(data={'email': email})
|
|
175 |
def record(cls, email_or_username):
|
|
176 |
super().record(data={'email_or_username': email_or_username})
|
|
177 | 177 | |
178 | 178 |
@classmethod |
179 | 179 |
def get_message(cls, event, context): |
180 |
email = event.get_data('email')
|
|
180 |
email_or_username = event.get_data('email_or_username')
|
|
181 | 181 |
if email: |
182 |
return _('password reset failure with email "%s"') % email
|
|
182 |
return _('password reset failure with email (or username) "%s"') % email_or_username
|
|
183 | 183 |
return super().get_message(event, context) |
184 | 184 | |
185 | 185 |
src/authentic2/locale/fr/LC_MESSAGES/django.po | ||
---|---|---|
7 | 7 |
msgstr "" |
8 | 8 |
"Project-Id-Version: Authentic\n" |
9 | 9 |
"Report-Msgid-Bugs-To: \n" |
10 |
"POT-Creation-Date: 2020-11-23 10:34+0100\n"
|
|
10 |
"POT-Creation-Date: 2020-12-04 13:46+0100\n"
|
|
11 | 11 |
"PO-Revision-Date: 2020-10-13 11:25+0200\n" |
12 | 12 |
"Last-Translator: Mikaël Ates <mates@entrouvert.com>\n" |
13 | 13 |
"Language-Team: None\n" |
... | ... | |
17 | 17 |
"Content-Transfer-Encoding: 8bit\n" |
18 | 18 |
"Plural-Forms: nplurals=2; plural=n>1;\n" |
19 | 19 | |
20 |
#: debian/multitenant/debian_config.py:40 src/authentic2/forms/passwords.py:39
|
|
20 |
#: debian/multitenant/debian_config.py:40 |
|
21 | 21 |
#: src/authentic2/forms/registration.py:40 src/authentic2/manager/forms.py:644 |
22 | 22 |
#: src/authentic2/manager/templates/authentic2/manager/user_create_registration_email_body.txt:4 |
23 | 23 |
#: src/authentic2/saml/models.py:134 |
... | ... | |
1148 | 1148 |
msgid "The image is not valid" |
1149 | 1149 |
msgstr "L’image n’est pas valide" |
1150 | 1150 | |
1151 |
#: src/authentic2/forms/passwords.py:95 src/authentic2/forms/passwords.py:108 |
|
1151 |
#: src/authentic2/forms/passwords.py:40 |
|
1152 |
msgid "Email or username" |
|
1153 |
msgstr "Courriel ou nom d'utilisateur" |
|
1154 | ||
1155 |
#: src/authentic2/forms/passwords.py:96 src/authentic2/forms/passwords.py:109 |
|
1152 | 1156 |
msgid "New password" |
1153 | 1157 |
msgstr "Nouveau mot de passe" |
1154 | 1158 | |
1155 |
#: src/authentic2/forms/passwords.py:96 src/authentic2/forms/passwords.py:109
|
|
1159 |
#: src/authentic2/forms/passwords.py:97 src/authentic2/forms/passwords.py:110
|
|
1156 | 1160 |
msgid "New password confirmation" |
1157 | 1161 |
msgstr "Confirmation du nouveau mot de passe" |
1158 | 1162 | |
1159 |
#: src/authentic2/forms/passwords.py:101 src/authentic2/forms/passwords.py:115
|
|
1163 |
#: src/authentic2/forms/passwords.py:102 src/authentic2/forms/passwords.py:116
|
|
1160 | 1164 |
msgid "New password must differ from old password" |
1161 | 1165 |
msgstr "Le nouveau mot de passe doit être différent de l’ancien." |
1162 | 1166 | |
1163 |
#: src/authentic2/forms/passwords.py:107
|
|
1167 |
#: src/authentic2/forms/passwords.py:108
|
|
1164 | 1168 |
msgid "Old password" |
1165 | 1169 |
msgstr "Ancien mot de passe" |
1166 | 1170 | |
... | ... | |
1406 | 1410 | |
1407 | 1411 |
#: src/authentic2/journal_event_types.py:182 |
1408 | 1412 |
#, python-format |
1409 |
msgid "password reset failure with email \"%s\"" |
|
1413 |
msgid "password reset failure with email (or username) \"%s\""
|
|
1410 | 1414 |
msgstr "" |
1411 |
"échec de réinitialisation du mot de passe pour l’adresse de courriel « %s »" |
|
1415 |
"échec de réinitialisation du mot de passe pour l’adresse de courriel (ou le " |
|
1416 |
"nom d'utilisateur) « %s »" |
|
1412 | 1417 | |
1413 | 1418 |
#: src/authentic2/journal_event_types.py:188 |
1414 | 1419 |
msgid "password change" |
... | ... | |
4651 | 4656 |
msgstr "Déconnecté" |
4652 | 4657 | |
4653 | 4658 |
#: src/authentic2/templates/registration/password_change_done.html:9 |
4654 |
#: src/authentic2/views.py:1302
|
|
4659 |
#: src/authentic2/views.py:1303
|
|
4655 | 4660 |
msgid "Password changed" |
4656 | 4661 |
msgstr "Mot de passe modifié" |
4657 | 4662 | |
... | ... | |
4692 | 4697 |
msgstr "Instructions de réinitialisation du mot de passe" |
4693 | 4698 | |
4694 | 4699 |
#: src/authentic2/templates/registration/password_reset_instructions.html:10 |
4695 |
#, python-format |
|
4696 | 4700 |
msgid "" |
4697 | 4701 |
"\n" |
4698 |
" If your email address exists in our database, an email has been sent to " |
|
4699 |
"%(email)s.\n" |
|
4702 |
" If a user account exists in our database with your email address (or " |
|
4703 |
"your\n" |
|
4704 |
" username), an email has been sent to you.\n" |
|
4700 | 4705 |
" " |
4701 | 4706 |
msgstr "" |
4702 | 4707 |
"\n" |
4703 |
" Si votre adresse électronique est présente dans notre base, un courriel "
|
|
4704 |
"a été envoyé à %(email)s.\n"
|
|
4708 |
" Si un compte utilisateur est présent dans notre base avec votre adresse "
|
|
4709 |
"électronique (ou votre nom d'utilisateur), un courriel vous a été envoyé.\n"
|
|
4705 | 4710 |
" " |
4706 | 4711 | |
4707 |
#: src/authentic2/templates/registration/password_reset_instructions.html:15
|
|
4712 |
#: src/authentic2/templates/registration/password_reset_instructions.html:16
|
|
4708 | 4713 |
msgid "" |
4709 | 4714 |
"\n" |
4710 | 4715 |
" Follow the instructions in this email in order to choose a new " |
... | ... | |
4716 | 4721 |
"de passe.\n" |
4717 | 4722 |
" " |
4718 | 4723 | |
4719 |
#: src/authentic2/templates/registration/password_reset_instructions.html:21
|
|
4724 |
#: src/authentic2/templates/registration/password_reset_instructions.html:22
|
|
4720 | 4725 |
#: src/authentic2/templates/registration/registration_complete.html:23 |
4721 | 4726 |
msgid "" |
4722 | 4727 |
"\n" |
... | ... | |
4729 | 4734 |
" également être considéré comme un pourriel (spam) : n’oubliez pas\n" |
4730 | 4735 |
" de regarder dans votre dossier « courriers indésirables ». " |
4731 | 4736 | |
4732 |
#: src/authentic2/templates/registration/password_reset_instructions.html:27
|
|
4737 |
#: src/authentic2/templates/registration/password_reset_instructions.html:28
|
|
4733 | 4738 |
#, python-format |
4734 | 4739 |
msgid "" |
4735 | 4740 |
"\n" |
... | ... | |
4746 | 4751 |
" puis recommencez la procédure de réinitialisation du mot de passe.\n" |
4747 | 4752 |
" " |
4748 | 4753 | |
4749 |
#: src/authentic2/templates/registration/password_reset_instructions.html:35
|
|
4754 |
#: src/authentic2/templates/registration/password_reset_instructions.html:36
|
|
4750 | 4755 |
msgid "Back to login" |
4751 | 4756 |
msgstr "Retour à la page de connexion" |
4752 | 4757 | |
... | ... | |
4805 | 4810 |
#: src/authentic2/templates/registration/registration_completion_choose.html:5 |
4806 | 4811 |
#: src/authentic2/templates/registration/registration_completion_form.html:5 |
4807 | 4812 |
#: src/authentic2/templates/registration/registration_completion_form.html:10 |
4808 |
#: src/authentic2/views.py:800
|
|
4813 |
#: src/authentic2/views.py:801
|
|
4809 | 4814 |
msgid "Registration" |
4810 | 4815 |
msgstr "Création d’un compte" |
4811 | 4816 | |
... | ... | |
5000 | 5005 |
"Cette page est périmée car vous vous êtes connecté entretemps; nous l’avons " |
5001 | 5006 |
"rechargée pour vous." |
5002 | 5007 | |
5003 |
#: src/authentic2/views.py:645 src/authentic2/views.py:730
|
|
5008 |
#: src/authentic2/views.py:645 src/authentic2/views.py:731
|
|
5004 | 5009 |
msgid "Password Reset" |
5005 | 5010 |
msgstr "Réinitialisation du mot de passe" |
5006 | 5011 | |
5007 |
#: src/authentic2/views.py:681 src/authentic2/views.py:834 |
|
5008 |
#, python-format |
|
5012 |
#: src/authentic2/views.py:682 |
|
5009 | 5013 |
msgid "" |
5010 |
"An email has already been sent to %s. Click \"Validate\" again if you really "
|
|
5011 |
"want it to be sent again." |
|
5014 |
"An email has already been sent to your address. Click \"Validate\" again if "
|
|
5015 |
"you really want it to be sent again."
|
|
5012 | 5016 |
msgstr "" |
5013 |
"Un courriel a déjà été envoyé à %s. Cliquez sur « Valider » si vous voulez "
|
|
5014 |
"vraiment qu’il soit réenvoyé." |
|
5017 |
"Un courriel a déjà été envoyé à votre adresse. Cliquez sur « Valider » si "
|
|
5018 |
"vous voulez vraiment qu’il soit réenvoyé."
|
|
5015 | 5019 | |
5016 |
#: src/authentic2/views.py:692 src/authentic2/views.py:844
|
|
5020 |
#: src/authentic2/views.py:693 src/authentic2/views.py:845
|
|
5017 | 5021 |
msgid "" |
5018 | 5022 |
"Multiple emails have already been sent to this address. Further attempts are " |
5019 | 5023 |
"blocked, please check your spam folder or try again later." |
... | ... | |
5022 | 5026 |
"envois sont bloqués, vous devriez vérifier votre dossier d’indésirables " |
5023 | 5027 |
"(spams, pourriels) ou réessayer plus tard." |
5024 | 5028 | |
5025 |
#: src/authentic2/views.py:701
|
|
5029 |
#: src/authentic2/views.py:702
|
|
5026 | 5030 |
msgid "" |
5027 | 5031 |
"Multiple password reset attempts have already been made from this IP " |
5028 | 5032 |
"address. No further email will be sent, please check your spam folder or try " |
... | ... | |
5033 | 5037 |
"devriez vérifier votre dossier d’indésirables (spams, pourriels) ou " |
5034 | 5038 |
"réessayer plus tard." |
5035 | 5039 | |
5036 |
#: src/authentic2/views.py:743
|
|
5040 |
#: src/authentic2/views.py:744
|
|
5037 | 5041 |
msgid "Password reset token is unknown or expired" |
5038 | 5042 |
msgstr "Le jeton de réinitialisation de mot de passe est inconnu ou expiré" |
5039 | 5043 | |
5040 |
#: src/authentic2/views.py:746
|
|
5044 |
#: src/authentic2/views.py:747
|
|
5041 | 5045 |
msgid "Password reset token is invalid" |
5042 | 5046 |
msgstr "Le jeton de réinitialisation de mot de passe est invalide" |
5043 | 5047 | |
5044 |
#: src/authentic2/views.py:754
|
|
5048 |
#: src/authentic2/views.py:755
|
|
5045 | 5049 |
msgid "User not found" |
5046 | 5050 |
msgstr "Utilisateur introuvable" |
5047 | 5051 | |
5048 |
#: src/authentic2/views.py:763
|
|
5052 |
#: src/authentic2/views.py:764
|
|
5049 | 5053 |
msgid "" |
5050 | 5054 |
"It's not possible to reset your password. Please contact an administrator." |
5051 | 5055 |
msgstr "" |
5052 | 5056 |
"Votre mot de passe n’a pas pu être réinitialisé. Veuillez contacter votre " |
5053 | 5057 |
"administrateur." |
5054 | 5058 | |
5055 |
#: src/authentic2/views.py:770
|
|
5059 |
#: src/authentic2/views.py:771
|
|
5056 | 5060 |
msgid "Enter new password" |
5057 | 5061 |
msgstr "Entrez un nouveau mot de passe" |
5058 | 5062 | |
5059 |
#: src/authentic2/views.py:852 |
|
5063 |
#: src/authentic2/views.py:835 |
|
5064 |
#, python-format |
|
5065 |
msgid "" |
|
5066 |
"An email has already been sent to %s. Click \"Validate\" again if you really " |
|
5067 |
"want it to be sent again." |
|
5068 |
msgstr "" |
|
5069 |
"Un courriel a déjà été envoyé à %s. Cliquez sur « Valider » si vous voulez " |
|
5070 |
"vraiment qu’il soit réenvoyé." |
|
5071 | ||
5072 |
#: src/authentic2/views.py:853 |
|
5060 | 5073 |
msgid "" |
5061 | 5074 |
"Multiple registration attempts have already been made from this IP address. " |
5062 | 5075 |
"No further email will be sent, please check your spam folder or try again " |
... | ... | |
5067 | 5080 |
"vérifier votre dossier d’indésirables (spams, pourriels) ou réessayer plus " |
5068 | 5081 |
"tard." |
5069 | 5082 | |
5070 |
#: src/authentic2/views.py:921
|
|
5083 |
#: src/authentic2/views.py:922
|
|
5071 | 5084 |
msgid "Your activation key is unknown or expired" |
5072 | 5085 |
msgstr "Votre clé d’activation est inconnue ou a expiré" |
5073 | 5086 | |
5074 |
#: src/authentic2/views.py:924
|
|
5087 |
#: src/authentic2/views.py:925
|
|
5075 | 5088 |
msgid "Activation failed" |
5076 | 5089 |
msgstr "Échec à l’activation du compte" |
5077 | 5090 | |
5078 |
#: src/authentic2/views.py:1186
|
|
5091 |
#: src/authentic2/views.py:1187
|
|
5079 | 5092 |
msgid "Request account deletion" |
5080 | 5093 |
msgstr "Demande de suppression de compte" |
5081 | 5094 | |
5082 |
#: src/authentic2/views.py:1198
|
|
5095 |
#: src/authentic2/views.py:1199
|
|
5083 | 5096 |
msgid "" |
5084 | 5097 |
"An account deletion validation email has been sent to your email address." |
5085 | 5098 |
msgstr "" |
5086 | 5099 |
"Un message pour valider la suppression du compte a été envoyé à votre " |
5087 | 5100 |
"adresse électronique." |
5088 | 5101 | |
5089 |
#: src/authentic2/views.py:1209
|
|
5102 |
#: src/authentic2/views.py:1210
|
|
5090 | 5103 |
msgid "Confirm account deletion" |
5091 | 5104 |
msgstr "Confirmation de la suppression du compte" |
5092 | 5105 | |
5093 |
#: src/authentic2/views.py:1221 src/authentic2/views.py:1235
|
|
5106 |
#: src/authentic2/views.py:1222 src/authentic2/views.py:1236
|
|
5094 | 5107 |
msgid "This account has previously been deleted." |
5095 | 5108 |
msgstr "Ce compte a déjà été supprimé." |
5096 | 5109 | |
5097 |
#: src/authentic2/views.py:1224
|
|
5110 |
#: src/authentic2/views.py:1225
|
|
5098 | 5111 |
msgid "This account is inactive, it cannot be deleted." |
5099 | 5112 |
msgstr "Ce compte est désactivé, il ne peut plus être supprimé." |
5100 | 5113 | |
5101 |
#: src/authentic2/views.py:1227
|
|
5114 |
#: src/authentic2/views.py:1228
|
|
5102 | 5115 |
msgid "The account deletion request is too old, try again" |
5103 | 5116 |
msgstr "La demande de suppression de compte est expirée." |
5104 | 5117 | |
5105 |
#: src/authentic2/views.py:1229
|
|
5118 |
#: src/authentic2/views.py:1230
|
|
5106 | 5119 |
msgid "The account deletion request is invalid, try again" |
5107 | 5120 |
msgstr "La demande de suppression de compte n’est pas valide." |
5108 | 5121 | |
5109 |
#: src/authentic2/views.py:1231
|
|
5122 |
#: src/authentic2/views.py:1232
|
|
5110 | 5123 |
msgid "The account deletion request was not on this site, try again" |
5111 | 5124 |
msgstr "" |
5112 | 5125 |
"Votre demande de suppression de compte vient d’un autre site que celui-ci." |
5113 | 5126 | |
5114 |
#: src/authentic2/views.py:1255
|
|
5127 |
#: src/authentic2/views.py:1256
|
|
5115 | 5128 |
msgid "Deletion performed." |
5116 | 5129 |
msgstr "Suppression effectuée." |
5117 | 5130 | |
5118 |
#: src/authentic2/views.py:1280
|
|
5131 |
#: src/authentic2/views.py:1281
|
|
5119 | 5132 |
msgid "Password Change" |
5120 | 5133 |
msgstr "Changement de mot de passe" |
5121 | 5134 | |
5122 |
#: src/authentic2/views.py:1291
|
|
5135 |
#: src/authentic2/views.py:1292
|
|
5123 | 5136 |
msgid "Password change is forbidden" |
5124 | 5137 |
msgstr "Changement de mot de passe interdit" |
5125 | 5138 | |
5126 |
#: src/authentic2/views.py:1336
|
|
5139 |
#: src/authentic2/views.py:1337
|
|
5127 | 5140 |
msgid "Consent Management" |
5128 | 5141 |
msgstr "Gestion des autorisations" |
src/authentic2/templates/registration/password_reset_instructions.html | ||
---|---|---|
7 | 7 | |
8 | 8 |
{% block content %} |
9 | 9 |
<p><strong> |
10 |
{% blocktrans with email=request.session.reset_email %} |
|
11 |
If your email address exists in our database, an email has been sent to {{ email }}. |
|
10 |
{% blocktrans %} |
|
11 |
If a user account exists in our database with your email address (or your |
|
12 |
username), an email has been sent to you. |
|
12 | 13 |
{% endblocktrans %} |
13 | 14 |
</strong></p> |
14 | 15 |
<p><strong> |
src/authentic2/utils/__init__.py | ||
---|---|---|
804 | 804 |
user.save() |
805 | 805 |
lifetime = settings.PASSWORD_RESET_TIMEOUT_DAYS * 3600 * 24 |
806 | 806 |
# invalidate any token associated with this user |
807 |
Token.objects.filter(kind='pw-reset', content__user=user.pk, content__email=user.email).delete() |
|
808 |
token = Token.create('pw-reset', {'user': user.pk, 'email': user.email}, duration=lifetime) |
|
807 |
Token.objects.filter(kind='pw-reset', content__user=user.pk, content__email=user.email, content__username=user.username).delete()
|
|
808 |
token = Token.create('pw-reset', {'user': user.pk, 'email': user.email, 'username': user.username}, duration=lifetime)
|
|
809 | 809 |
reset_url = make_url( |
810 | 810 |
'password_reset_confirm', |
811 | 811 |
kwargs={'token': token.uuid_b64url}, |
src/authentic2/views.py | ||
---|---|---|
667 | 667 |
return ctx |
668 | 668 | |
669 | 669 |
def form_valid(self, form): |
670 |
email = form.cleaned_data['email']
|
|
670 |
email_or_username = form.cleaned_data['email_or_username']
|
|
671 | 671 | |
672 | 672 |
# if an email has already been sent, warn once before allowing resend |
673 | 673 |
token = models.Token.objects.filter( |
674 |
kind='pw-reset', content__email=email, expires__gt=timezone.now() |
|
674 |
Q(content__email=email_or_username) | Q(content__username=email_or_username), |
|
675 |
kind='pw-reset', expires__gt=timezone.now() |
|
675 | 676 |
).exists() |
676 | 677 |
resend_key = 'pw-reset-allow-resend' |
677 | 678 |
if app_settings.A2_TOKEN_EXISTS_WARNING and token and not self.request.session.get(resend_key): |
678 | 679 |
self.request.session[resend_key] = True |
679 | 680 |
form.add_error( |
680 |
'email', |
|
681 |
_('An email has already been sent to %s. Click "Validate" again if '
|
|
682 |
'you really want it to be sent again.') % email
|
|
681 |
'email_or_username',
|
|
682 |
_('An email has already been sent to your address. Click "Validate" again if '
|
|
683 |
'you really want it to be sent again.') |
|
683 | 684 |
) |
684 | 685 |
return self.form_invalid(form) |
685 | 686 |
self.request.session[resend_key] = False |
686 | 687 | |
687 |
if is_ratelimited(self.request, key='post:email', group='pw-reset-email',
|
|
688 |
if is_ratelimited(self.request, key='post:email_or_username', group='pw-reset-email-or-username',
|
|
688 | 689 |
rate=app_settings.A2_EMAILS_ADDRESS_RATELIMIT, increment=True): |
689 |
self.request.journal.record('user.password.reset.failure', email=email)
|
|
690 |
self.request.journal.record('user.password.reset.failure', email_or_username=email_or_username)
|
|
690 | 691 |
form.add_error( |
691 |
'email', |
|
692 |
'email_or_username',
|
|
692 | 693 |
_('Multiple emails have already been sent to this address. Further attempts are ' |
693 | 694 |
'blocked, please check your spam folder or try again later.') |
694 | 695 |
) |
695 | 696 |
return self.form_invalid(form) |
696 |
if is_ratelimited(self.request, key='ip', group='pw-reset-email', |
|
697 |
if is_ratelimited(self.request, key='ip', group='pw-reset-email-or-username',
|
|
697 | 698 |
rate=app_settings.A2_EMAILS_IP_RATELIMIT, increment=True): |
698 |
self.request.journal.record('user.password.reset.failure', email=email)
|
|
699 |
self.request.journal.record('user.password.reset.failure', email_or_username=email_or_username)
|
|
699 | 700 |
form.add_error( |
700 |
'email', |
|
701 |
'email_or_username',
|
|
701 | 702 |
_('Multiple password reset attempts have already been made from this IP address. No ' |
702 | 703 |
'further email will be sent, please check your spam folder or try again later.') |
703 | 704 |
) |
704 | 705 |
return self.form_invalid(form) |
705 | 706 | |
706 | 707 |
form.save() |
707 |
self.request.session['reset_email'] = email
|
|
708 |
self.request.session['reset_email_or_username'] = email_or_username
|
|
708 | 709 |
return super(PasswordResetView, self).form_valid(form) |
709 | 710 | |
710 | 711 |
password_reset = PasswordResetView.as_view() |
tests/auth_fc/test_auth_fc.py | ||
---|---|---|
211 | 211 |
# No FC account, forbidden to set a password |
212 | 212 |
response = app.get('/login/') |
213 | 213 |
response = response.click('Reset it!').maybe_follow() |
214 |
response.form['email'] = user.email |
|
214 |
response.form['email_or_username'] = user.email
|
|
215 | 215 |
assert len(mailoutbox) == 0 |
216 | 216 |
response = response.form.submit() |
217 | 217 |
assert len(mailoutbox) == 1 |
... | ... | |
224 | 224 |
models.FcAccount.objects.create(user=user, sub='xxx', token='aaa') |
225 | 225 |
response = app.get('/login/') |
226 | 226 |
response = response.click('Reset it!').maybe_follow() |
227 |
response.form['email'] = user.email |
|
227 |
response.form['email_or_username'] = user.email
|
|
228 | 228 |
assert len(mailoutbox) == 1 |
229 | 229 |
response = response.form.submit() |
230 | 230 |
assert len(mailoutbox) == 2 |
tests/test_ldap.py | ||
---|---|---|
705 | 705 |
else: |
706 | 706 |
response = response.maybe_follow() |
707 | 707 |
response = response.click('Reset it!') |
708 |
response.form['email'] = EMAIL |
|
708 |
response.form['email_or_username'] = EMAIL
|
|
709 | 709 |
assert len(mail.outbox) == 0 |
710 | 710 |
response = response.form.submit().maybe_follow() |
711 | 711 |
assert len(mail.outbox) == 1 |
tests/test_password_reset.py | ||
---|---|---|
43 | 43 |
utils.assert_event('user.password.reset', user=simple_user, session=app.session) |
44 | 44 | |
45 | 45 | |
46 |
def test_view(app, simple_user, mailoutbox, settings): |
|
46 |
def test_view_with_email(app, simple_user, mailoutbox, settings):
|
|
47 | 47 |
url = reverse('password_reset') |
48 | 48 |
resp = app.get(url, status=200) |
49 |
resp.form.set('email', simple_user.email) |
|
49 |
resp.form.set('email_or_username', simple_user.email) |
|
50 |
assert len(mailoutbox) == 0 |
|
51 |
settings.DEFAULT_FROM_EMAIL = 'show only addr <noreply@example.net>' |
|
52 |
resp = resp.form.submit() |
|
53 |
utils.assert_event('user.password.reset.request', user=simple_user, email=simple_user.email) |
|
54 |
assert resp['Location'].endswith('/instructions/') |
|
55 |
resp = resp.follow() |
|
56 |
assert '"noreply@example.net"' in resp.text |
|
57 |
assert 'show only addr' not in resp.text |
|
58 |
assert len(mailoutbox) == 1 |
|
59 |
url = utils.get_link_from_mail(mailoutbox[0]) |
|
60 |
relative_url = url.split('testserver')[1] |
|
61 |
resp = app.get(relative_url, status=200) |
|
62 |
resp.form.set('new_password1', '1234==aA') |
|
63 |
resp.form.set('new_password2', '1234==aA') |
|
64 |
resp = resp.form.submit() |
|
65 |
# verify user is logged |
|
66 |
assert str(app.session['_auth_user_id']) == str(simple_user.pk) |
|
67 | ||
68 |
with override_settings(A2_USER_CAN_RESET_PASSWORD=False): |
|
69 |
url = reverse('password_reset') |
|
70 |
app.get(url, status=404) |
|
71 | ||
72 | ||
73 |
def test_view_with_username(app, simple_user, mailoutbox, settings): |
|
74 |
url = reverse('password_reset') |
|
75 |
resp = app.get(url, status=200) |
|
76 |
resp.form.set('email_or_username', simple_user.username) |
|
50 | 77 |
assert len(mailoutbox) == 0 |
51 | 78 |
settings.DEFAULT_FROM_EMAIL = 'show only addr <noreply@example.net>' |
52 | 79 |
resp = resp.form.submit() |
53 | 80 |
utils.assert_event('user.password.reset.request', user=simple_user, email=simple_user.email) |
54 | 81 |
assert resp['Location'].endswith('/instructions/') |
55 | 82 |
resp = resp.follow() |
56 |
assert simple_user.email in resp.text |
|
57 | 83 |
assert '"noreply@example.net"' in resp.text |
58 | 84 |
assert 'show only addr' not in resp.text |
59 | 85 |
assert len(mailoutbox) == 1 |
... | ... | |
76 | 102 | |
77 | 103 |
url = reverse('password_reset') |
78 | 104 |
resp = app.get(url, status=200) |
79 |
resp.form.set('email', simple_user.email) |
|
105 |
resp.form.set('email_or_username', simple_user.email)
|
|
80 | 106 |
assert len(mailoutbox) == 0 |
81 | 107 |
resp = resp.form.submit() |
82 | 108 |
assert len(mailoutbox) == 0 |
... | ... | |
87 | 113 | |
88 | 114 |
url = reverse('password_reset') |
89 | 115 |
resp = app.get(url, status=200) |
90 |
resp.form.set('email', simple_user.email) |
|
116 |
resp.form.set('email_or_username', simple_user.email)
|
|
91 | 117 |
assert len(mailoutbox) == 0 |
92 | 118 |
resp = resp.form.submit() |
93 | 119 |
assert len(mailoutbox) == 0 |
tests/test_views.py | ||
---|---|---|
181 | 181 |
settings.A2_EMAILS_ADDRESS_RATELIMIT = '3/d' |
182 | 182 |
users = [User.objects.create(email='test%s@test.com' % i) for i in range(8)] |
183 | 183 | |
184 |
email_field = 'email_or_username' if view_name == 'password_reset' else 'email' |
|
185 | ||
184 | 186 |
# reach email limit |
185 | 187 |
for _ in range(3): |
186 | 188 |
response = app.get(reverse(view_name)) |
187 |
response.form.set('email', simple_user.email)
|
|
189 |
response.form.set(email_field, simple_user.email)
|
|
188 | 190 |
response = response.form.submit() |
189 | 191 |
assert len(mailoutbox) == 3 |
190 | 192 | |
191 | 193 |
response = app.get(reverse(view_name)) |
192 |
response.form.set('email', simple_user.email)
|
|
194 |
response.form.set(email_field, simple_user.email)
|
|
193 | 195 |
response = response.form.submit() |
194 | 196 |
assert len(mailoutbox) == 3 |
195 | 197 |
assert 'try again later' in response.text |
196 | 198 |
if view_name == 'password_reset': |
197 |
assert_event('user.password.reset.failure', email=simple_user.email) |
|
199 |
assert_event('user.password.reset.failure', email_or_username=simple_user.email)
|
|
198 | 200 | |
199 | 201 |
# reach ip limit |
200 | 202 |
for i in range(7): |
201 | 203 |
response = app.get(reverse(view_name)) |
202 |
response.form.set('email', users[i].email)
|
|
204 |
response.form.set(email_field, users[i].email)
|
|
203 | 205 |
response = response.form.submit() |
204 | 206 |
assert len(mailoutbox) == 10 |
205 | 207 | |
206 | 208 |
response = app.get(reverse(view_name)) |
207 |
response.form.set('email', users[i + 1].email)
|
|
209 |
response.form.set(email_field, users[i + 1].email)
|
|
208 | 210 |
response = response.form.submit() |
209 | 211 |
assert len(mailoutbox) == 10 |
210 | 212 |
assert 'try again later' in response.text |
... | ... | |
212 | 214 |
# ip ratelimits are lifted after an hour |
213 | 215 |
freezer.tick(datetime.timedelta(hours=1)) |
214 | 216 |
response = app.get(reverse(view_name)) |
215 |
response.form.set('email', users[0].email)
|
|
217 |
response.form.set(email_field, users[0].email)
|
|
216 | 218 |
response = response.form.submit() |
217 | 219 |
assert len(mailoutbox) == 11 |
218 | 220 | |
219 | 221 |
# email ratelimits are lifted after a day |
220 | 222 |
response = app.get(reverse(view_name)) |
221 |
response.form.set('email', simple_user.email)
|
|
223 |
response.form.set(email_field, simple_user.email)
|
|
222 | 224 |
response = response.form.submit() |
223 | 225 |
assert len(mailoutbox) == 11 |
224 | 226 |
assert 'try again later' in response.text |
225 | 227 | |
226 | 228 |
freezer.tick(datetime.timedelta(days=1)) |
227 | 229 |
response = app.get(reverse(view_name)) |
228 |
response.form.set('email', simple_user.email)
|
|
230 |
response.form.set(email_field, simple_user.email)
|
|
229 | 231 |
response = response.form.submit() |
230 | 232 |
assert len(mailoutbox) == 12 |
231 | 233 | |
... | ... | |
234 | 236 |
def test_views_email_token_resend(app, simple_user, settings, mailoutbox, view_name): |
235 | 237 |
settings.A2_TOKEN_EXISTS_WARNING = True |
236 | 238 | |
239 |
email_field = 'email_or_username' if view_name == 'password_reset' else 'email' |
|
240 | ||
237 | 241 |
response = app.get(reverse(view_name)) |
238 |
response.form.set('email', simple_user.email)
|
|
242 |
response.form.set(email_field, simple_user.email)
|
|
239 | 243 |
response = response.form.submit() |
240 | 244 |
assert len(mailoutbox) == 1 |
241 | 245 | |
242 | 246 |
# warn user token has already been sent |
243 | 247 |
response = app.get(reverse(view_name)) |
244 |
response.form.set('email', simple_user.email)
|
|
248 |
response.form.set(email_field, simple_user.email)
|
|
245 | 249 |
response = response.form.submit() |
246 | 250 |
assert 'email has already been sent' in response.text |
247 | 251 |
assert len(mailoutbox) == 1 |
248 |
- |