mirror of https://github.com/zulip/zulip.git
reset_password: Modify password reset email if email is in wrong realm.
This fixes a confusing issue where a user might try resetting the password for an email account that in part of a different Zulip organization. Is a useful early step towards making Zulip support reusing an email in multiple realms. Fixes: #4557.
This commit is contained in:
parent
a8521dc988
commit
556264f3d7
|
@ -1,8 +1,15 @@
|
|||
{% if attempted_realm %}
|
||||
Someone (possibly you) requested a password reset email for {{ email }} on
|
||||
{{ attempted_realm.uri }}, but {{ email }} does not
|
||||
have an active account in {{ attempted_realm.uri }}. However, {{ email }} does
|
||||
have an active account in {{ user.realm.uri }} organization; you
|
||||
can try logging in or resetting your password there.
|
||||
{% else %}
|
||||
Psst. Word on the street is that you forgot your password, {{ email }}.
|
||||
|
||||
It's all good. Follow the link below and we'll take care of the rest:
|
||||
|
||||
{{ protocol }}://{{ user.realm.host }}{{ url('django.contrib.auth.views.password_reset_confirm', kwargs=dict(uidb64=uid, token=token)) }}
|
||||
|
||||
{% endif %}
|
||||
Thanks,
|
||||
Your friends at Zulip HQ
|
||||
|
|
|
@ -25,7 +25,7 @@ import logging
|
|||
import re
|
||||
import DNS
|
||||
|
||||
from typing import Any, Callable, List, Optional, Text
|
||||
from typing import Any, Callable, List, Optional, Text, Dict
|
||||
|
||||
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>. ' + \
|
||||
|
@ -185,6 +185,48 @@ class ZulipPasswordResetForm(PasswordResetForm):
|
|||
logging.info("Password reset attempted for %s; no active account." % (email,))
|
||||
return result
|
||||
|
||||
def send_mail(self, subject_template_name, email_template_name,
|
||||
context, from_email, to_email, html_email_template_name=None):
|
||||
# type: (str, str, Dict[str, Any], str, str, str) -> None
|
||||
"""
|
||||
Currently we don't support accounts in multiple subdomains using
|
||||
a single email addresss. We override this function so that we do
|
||||
not send a reset link to an email address if the reset attempt is
|
||||
done on the subdomain which does not match user.realm.subdomain.
|
||||
|
||||
Once we start supporting accounts with the same email in
|
||||
multiple subdomains, we may be able to delete or refactor this
|
||||
function.
|
||||
"""
|
||||
user_realm = get_user_profile_by_email(to_email).realm
|
||||
attempted_subdomain = get_subdomain(getattr(self, 'request'))
|
||||
context['attempted_realm'] = False
|
||||
if not check_subdomain(user_realm.subdomain, attempted_subdomain):
|
||||
context['attempted_realm'] = get_realm(attempted_subdomain)
|
||||
|
||||
super(ZulipPasswordResetForm, self).send_mail(
|
||||
subject_template_name,
|
||||
email_template_name,
|
||||
context,
|
||||
from_email,
|
||||
to_email,
|
||||
html_email_template_name=html_email_template_name
|
||||
)
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# type: (*Any, **Any) -> None
|
||||
"""Currently we don't support accounts in multiple subdomains using
|
||||
a single email addresss. We override this function so that we can
|
||||
inject request parameter in context. This parameter will be used
|
||||
by send_mail function.
|
||||
|
||||
Once we start supporting accounts with the same email in
|
||||
multiple subdomains, we may be able to delete or refactor this
|
||||
function.
|
||||
"""
|
||||
setattr(self, 'request', kwargs.get('request'))
|
||||
super(ZulipPasswordResetForm, self).save(*args, **kwargs)
|
||||
|
||||
class CreateUserForm(forms.Form):
|
||||
full_name = forms.CharField(max_length=100)
|
||||
email = forms.EmailField()
|
||||
|
|
|
@ -32,7 +32,8 @@ from zerver.management.commands.deliver_email import send_email_job
|
|||
from zerver.lib.actions import (
|
||||
set_default_streams,
|
||||
do_change_is_admin,
|
||||
get_stream
|
||||
get_stream,
|
||||
do_create_realm,
|
||||
)
|
||||
|
||||
from zerver.lib.initial_password import initial_password
|
||||
|
@ -151,6 +152,63 @@ class PasswordResetTest(ZulipTestCase):
|
|||
# make sure old password no longer works
|
||||
self.login(email, password=old_password, fails=True)
|
||||
|
||||
def test_invalid_subdomain(self):
|
||||
# type: () -> None
|
||||
email = 'hamlet@zulip.com'
|
||||
string_id = 'hamlet'
|
||||
name = 'Hamlet'
|
||||
do_create_realm(
|
||||
string_id,
|
||||
name,
|
||||
restricted_to_domain=False,
|
||||
invite_required=False
|
||||
)
|
||||
|
||||
with self.settings(REALMS_HAVE_SUBDOMAINS=True):
|
||||
with patch('zerver.forms.get_subdomain', return_value=string_id):
|
||||
# start the password reset process by supplying an email address
|
||||
result = self.client_post(
|
||||
'/accounts/password/reset/', {'email': email})
|
||||
|
||||
# check the redirect link telling you to check mail for password reset link
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertTrue(result["Location"].endswith(
|
||||
"/accounts/password/reset/done/"))
|
||||
result = self.client_get(result["Location"])
|
||||
|
||||
self.assert_in_response("Check your email to finish the process.", result)
|
||||
|
||||
from django.core.mail import outbox
|
||||
self.assertEqual(len(outbox), 1)
|
||||
message = outbox.pop()
|
||||
self.assertIn("hamlet@zulip.com does not\nhave an active account in http://",
|
||||
message.body)
|
||||
|
||||
def test_correct_subdomain(self):
|
||||
# type: () -> None
|
||||
email = 'hamlet@zulip.com'
|
||||
string_id = 'zulip'
|
||||
|
||||
with self.settings(REALMS_HAVE_SUBDOMAINS=True):
|
||||
with patch('zerver.forms.get_subdomain', return_value=string_id):
|
||||
# start the password reset process by supplying an email address
|
||||
result = self.client_post(
|
||||
'/accounts/password/reset/', {'email': email})
|
||||
|
||||
# check the redirect link telling you to check mail for password reset link
|
||||
self.assertEqual(result.status_code, 302)
|
||||
self.assertTrue(result["Location"].endswith(
|
||||
"/accounts/password/reset/done/"))
|
||||
result = self.client_get(result["Location"])
|
||||
|
||||
self.assert_in_response("Check your email to finish the process.", result)
|
||||
|
||||
from django.core.mail import outbox
|
||||
self.assertEqual(len(outbox), 1)
|
||||
message = outbox.pop()
|
||||
self.assertIn("Psst. Word on the street is that you forgot your password,",
|
||||
message.body)
|
||||
|
||||
def test_redirect_endpoints(self):
|
||||
# type: () -> None
|
||||
'''
|
||||
|
|
Loading…
Reference in New Issue