warn users on password change confirmation (#78111) #114
|
@ -241,12 +241,15 @@ class PasswordResetMixin(Form):
|
|||
class NotifyOfPasswordChange:
|
||||
def save(self, commit=True):
|
||||
user = super().save(commit=commit)
|
||||
if user.email:
|
||||
authn = get_password_authenticator()
|
||||
if user.email and user.email_verified:
|
||||
ctx = {
|
||||
'user': user,
|
||||
'password': self.cleaned_data['new_password1'],
|
||||
}
|
||||
utils_misc.send_templated_mail(user, 'authentic2/password_change', ctx)
|
||||
elif authn.is_phone_authn_active and (phone := user.phone_identifier) and user.phone_verified_on:
|
||||
utils_sms.send_password_reset_confirmation_sms(phone, user.ou)
|
||||
return user
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
{% load i18n %}{% blocktrans %}Your password has been changed. If you are not the author of the change request, please contact an administrator.{% endblocktrans %}
|
|
@ -84,10 +84,13 @@ def send_sms(phone_number, ou, user=None, template_names=None, context=None, kin
|
|||
if not isinstance(context, dict):
|
||||
context = {}
|
||||
|
||||
code = generate_code(phone_number, user=user, kind=kind)
|
||||
if code.fake is True:
|
||||
return code
|
||||
context.update({'code': code})
|
||||
code = None
|
||||
if kind is not None:
|
||||
# SMS with a specific action requires generating a code
|
||||
code = generate_code(phone_number, user=user, kind=kind)
|
||||
if code.fake is True:
|
||||
return code
|
||||
context.update({'code': code})
|
||||
|
||||
message = render_plain_text_template_to_string(template_names, context)
|
||||
|
||||
|
@ -101,7 +104,6 @@ def send_sms(phone_number, ou, user=None, template_names=None, context=None, kin
|
|||
with transaction.atomic():
|
||||
response = requests.post(url, json=payload, timeout=10)
|
||||
response.raise_for_status()
|
||||
code.save()
|
||||
except RequestException as e:
|
||||
logger.warning('sms code to %s using %s failed: %s', phone_number, url, e)
|
||||
raise SMSError(f'Error while contacting SMS service: {e}')
|
||||
|
@ -121,6 +123,15 @@ def send_registration_sms(phone_number, ou, template_names=None, context=None, *
|
|||
)
|
||||
|
||||
|
||||
def send_password_reset_confirmation_sms(phone_number, ou, context=None):
|
||||
return send_sms(
|
||||
phone_number,
|
||||
ou,
|
||||
template_names=['password_lost/sms_password_change_confirmation.txt'],
|
||||
context=context,
|
||||
)
|
||||
|
||||
|
||||
def send_account_deletion_sms(phone_number, ou, user=None, template_names=None, context=None, **kwargs):
|
||||
from authentic2.models import SMSCode
|
||||
|
||||
|
|
|
@ -1319,6 +1319,7 @@ class PasswordResetConfirmView(cbv.RedirectToNextURLViewMixin, FormView):
|
|||
def dispatch(self, request, *args, **kwargs):
|
||||
token = kwargs['token'].replace(' ', '')
|
||||
self.next_url = utils_misc.select_next_url(request, None)
|
||||
self.authenticator = utils_misc.get_password_authenticator()
|
||||
try:
|
||||
self.token = models.Token.use('pw-reset', token, delete=False)
|
||||
except models.Token.DoesNotExist:
|
||||
|
@ -1363,8 +1364,11 @@ class PasswordResetConfirmView(cbv.RedirectToNextURLViewMixin, FormView):
|
|||
return kwargs
|
||||
|
||||
def form_valid(self, form):
|
||||
# Changing password by mail validate the email
|
||||
form.user.set_email_verified(True, source='user')
|
||||
# Changing password by mail validate the user's known identifier
|
||||
if self.token.content.get('email'):
|
||||
form.user.set_email_verified(True, source='user')
|
||||
elif self.token.content.get('phone'):
|
||||
form.user.phone_verified_on = timezone.now()
|
||||
form.save()
|
||||
hooks.call_hooks('event', name='password-reset-confirm', user=form.user, token=self.token, form=form)
|
||||
logger.info('password reset for user %s with token %r', self.user, self.token.uuid)
|
||||
|
|
|
@ -56,6 +56,7 @@ def test_send_password_reset_email(app, simple_user, mailoutbox):
|
|||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp = resp.form.submit().follow()
|
||||
assert len(mailoutbox) == 2
|
||||
assert str(app.session['_auth_user_id']) == str(simple_user.pk)
|
||||
utils.assert_event('user.password.reset', user=simple_user, session=app.session)
|
||||
|
||||
|
@ -104,7 +105,10 @@ def test_send_password_reset_by_sms_code(app, nomail_user, settings, phone_activ
|
|||
assert authenticate(username='user', password='1234==aA') is None
|
||||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp.form.submit()
|
||||
with HTTMock(sms_service_mock):
|
||||
resp.form.submit()
|
||||
assert sms_service_mock.call['count'] == 1
|
||||
assert SMSCode.objects.count() == 1 # no new code generated
|
||||
# verify user is logged
|
||||
assert str(app.session['_auth_user_id']) == str(nomail_user.pk)
|
||||
user = authenticate(username='user', password='1234==aA')
|
||||
|
@ -115,7 +119,9 @@ def test_send_password_reset_by_sms_code(app, nomail_user, settings, phone_activ
|
|||
app.get(url, status=404)
|
||||
|
||||
|
||||
def test_send_password_reset_by_sms_code_nondefault_attribute(app, nomail_user, simple_user, settings):
|
||||
def test_send_password_reset_by_sms_code_nondefault_attribute(
|
||||
app, nomail_user, simple_user, settings, phone_activated_authn
|
||||
):
|
||||
phone, dummy = Attribute.objects.get_or_create(
|
||||
name='another_phone',
|
||||
kind='phone_number',
|
||||
|
@ -142,7 +148,10 @@ def test_send_password_reset_by_sms_code_nondefault_attribute(app, nomail_user,
|
|||
|
||||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp.form.submit()
|
||||
with HTTMock(sms_service_mock):
|
||||
resp.form.submit()
|
||||
assert sms_service_mock.call['count'] == 1
|
||||
assert SMSCode.objects.count() == 1 # no new code generated
|
||||
# verify user is logged
|
||||
assert str(app.session['_auth_user_id']) == str(nomail_user.pk)
|
||||
user = authenticate(username='user', password='1234==aA')
|
||||
|
@ -167,7 +176,10 @@ def test_send_password_reset_by_sms_code_nondefault_attribute(app, nomail_user,
|
|||
|
||||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp.form.submit()
|
||||
with HTTMock(sms_service_mock):
|
||||
resp.form.submit()
|
||||
assert sms_service_mock.call['count'] == 1
|
||||
assert SMSCode.objects.count() == 1 # no new code generated
|
||||
# verify user is logged
|
||||
assert str(app.session['_auth_user_id']) == str(simple_user.pk)
|
||||
user = authenticate(username='simpleuser', password='1234==aA')
|
||||
|
@ -194,7 +206,10 @@ def test_send_password_reset_by_sms_code_next_url(app, nomail_user, settings, ph
|
|||
|
||||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp = resp.form.submit()
|
||||
with HTTMock(sms_service_mock):
|
||||
resp = resp.form.submit()
|
||||
assert sms_service_mock.call['count'] == 1
|
||||
assert SMSCode.objects.count() == 1 # no new code generated
|
||||
user = authenticate(username='user', password='1234==aA')
|
||||
assert user == nomail_user
|
||||
assert resp.location == '/accounts/consents/'
|
||||
|
@ -307,6 +322,7 @@ def test_can_reset_by_username(app, db, simple_user, settings, mailoutbox):
|
|||
resp.form.set('new_password1', '1234==aA')
|
||||
resp.form.set('new_password2', '1234==aA')
|
||||
resp = resp.form.submit()
|
||||
assert len(mailoutbox) == 2
|
||||
# verify user is logged
|
||||
assert str(app.session['_auth_user_id']) == str(simple_user.pk)
|
||||
|
||||
|
|
Loading…
Reference in New Issue