Added the ability for logged-in users to change their login (username) to any of the active email addresses of the account. Fixes ticket #2052.

- Legacy-Id: 12843
This commit is contained in:
Henrik Levkowetz 2017-02-15 16:59:23 +00:00
parent 44ad914fba
commit efc77762be
7 changed files with 167 additions and 6 deletions

View file

@ -173,7 +173,6 @@ from django import forms
class ChangePasswordForm(forms.Form):
current_password = forms.CharField(widget=forms.PasswordInput)
new_password = forms.CharField(widget=PasswordStrengthInput)
new_password_confirmation = forms.CharField(widget=PasswordConfirmationInput)
@ -185,10 +184,29 @@ class ChangePasswordForm(forms.Form):
password = self.cleaned_data.get('current_password', None)
if not self.user.check_password(password):
raise ValidationError('Invalid password')
return password
def clean(self):
new_password = self.cleaned_data.get('new_password', None)
conf_password = self.cleaned_data.get('new_password_confirmation', None)
if not new_password == conf_password:
raise ValidationError("The password confirmation is different than the new password")
class ChangeUsernameForm(forms.Form):
username = forms.ChoiceField(choices=['-','--------'])
password = forms.CharField(widget=forms.PasswordInput, help_text="Confirm the change with your password")
def __init__(self, user, *args, **kwargs):
assert isinstance(user, User)
super(ChangeUsernameForm, self).__init__(*args, **kwargs)
self.user = user
emails = user.person.email_set.filter(active=True)
choices = [ (email.address, email.address) for email in emails ]
self.fields['username'] = forms.ChoiceField(choices=choices)
def clean_password(self):
password = self.cleaned_data['password']
if not self.user.check_password(password):
raise ValidationError('Invalid password')
return password

View file

@ -449,3 +449,50 @@ class IetfAuthTests(TestCase):
user = User.objects.get(username="someone@example.com")
self.assertTrue(user.check_password(u'foobar'))
def test_change_username(self):
chun_url = urlreverse(ietf.ietfauth.views.change_username)
prof_url = urlreverse(ietf.ietfauth.views.profile)
login_url = urlreverse(django.contrib.auth.views.login)
redir_url = '%s?next=%s' % (login_url, chun_url)
# get without logging in
r = self.client.get(chun_url)
self.assertRedirects(r, redir_url)
user = User.objects.create(username="someone@example.com", email="someone@example.com")
user.set_password("password")
user.save()
p = Person.objects.create(name="Some One", ascii="Some One", user=user)
Email.objects.create(address=user.username, person=p)
Email.objects.create(address="othername@example.org", person=p)
# log in
r = self.client.post(redir_url, {"username":user.username, "password":"password"})
self.assertRedirects(r, chun_url)
# wrong username
r = self.client.post(chun_url, {"username": "fiddlesticks",
"password": "password",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'username',
"Select a valid choice. fiddlesticks is not one of the available choices.")
# wrong password
r = self.client.post(chun_url, {"username": "othername@example.org",
"password": "foobar",
})
self.assertEqual(r.status_code, 200)
self.assertFormError(r, 'form', 'password', 'Invalid password')
# correct username change
r = self.client.post(chun_url, {"username": "othername@example.org",
"password": "password",
})
self.assertRedirects(r, prof_url)
# refresh user object
prev = user
user = User.objects.get(username="othername@example.org")
self.assertEqual(prev, user)
self.assertTrue(user.check_password(u'password'))

View file

@ -18,5 +18,6 @@ urlpatterns = [
url(r'^reset/confirm/(?P<auth>[^/]+)/$', views.confirm_password_reset),
url(r'^review/$', views.review_overview),
url(r'^testemail/$', views.test_email),
url(r'whitelist/add/?$', views.add_account_whitelist),
url(r'^username/$', views.change_username),
url(r'^whitelist/add/?$', views.add_account_whitelist),
]

View file

@ -52,8 +52,9 @@ from django.shortcuts import render, redirect, get_object_or_404
import debug # pyflakes:ignore
from ietf.group.models import Role, Group
from ietf.ietfauth.forms import RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm, WhitelistForm, ChangePasswordForm
from ietf.ietfauth.forms import get_person_form, RoleEmailForm, NewEmailForm
from ietf.ietfauth.forms import ( RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm,
WhitelistForm, ChangePasswordForm, get_person_form, RoleEmailForm,
NewEmailForm, ChangeUsernameForm )
from ietf.ietfauth.htpasswd import update_htpasswd_file
from ietf.ietfauth.utils import role_required
from ietf.mailinglists.models import Subscribed, Whitelisted
@ -521,3 +522,45 @@ def change_password(request):
})
@login_required
def change_username(request):
person = None
try:
person = request.user.person
except Person.DoesNotExist:
return render(request, 'registration/missing_person.html')
emails = [ e.address for e in Email.objects.filter(person=person, active=True) ]
emailz = [ e.address for e in person.email_set.filter(active=True) ]
assert emails == emailz
user = request.user
if request.method == 'POST':
form = ChangeUsernameForm(user, request.POST)
if form.is_valid():
new_username = form.cleaned_data["username"]
password = form.cleaned_data["password"]
assert new_username in emails
user.username = new_username.lower()
user.save()
# password is also stored in htpasswd file
update_htpasswd_file(user.username, password)
# keep the session
update_session_auth_hash(request, user)
send_mail(request, emails, None, "Datatracker username change notification", "registration/username_change_email.txt", {})
messages.success(request, "Your username was successfully changed")
return HttpResponseRedirect(urlreverse('ietf.ietfauth.views.profile'))
else:
form = ChangeUsernameForm(request.user)
return render(request, 'registration/change_username.html', {
'form': form,
'user': user,
})

View file

@ -17,17 +17,19 @@
{% if user.is_authenticated %}
<li><a rel="nofollow" href="/accounts/logout/" >Sign out</a></li>
<li><a rel="nofollow" href="/accounts/profile/">Account info</a></li>
<li><a href="{%url "ietf.cookies.views.preferences" %}" rel="nofollow">Preferences</a></li>
<li><a rel="nofollow" href="/accounts/password/">Change password</a></li>
<li><a rel="nofollow" href="/accounts/username/">Change username</a></li>
{% else %}
<li><a rel="nofollow" href="/accounts/login/?next={{request.get_full_path|urlencode}}">Sign in</a></li>
<li><a rel="nofollow" href="/accounts/reset/">Password reset</a></li>
<li><a href="{%url "ietf.cookies.views.preferences" %}" rel="nofollow">Preferences</a></li>
{% endif %}
{% endif %}
{% if not request.user.is_authenticated %}
<li><a href="{% url "ietf.ietfauth.views.create_account" %}">New account</a></li>
{% endif %}
<li><a href="{%url "ietf.cookies.views.preferences" %}" rel="nofollow">Preferences</a></li>
{% if user|has_role:"Reviewer" %}
<li><a href="{% url "ietf.ietfauth.views.review_overview" %}">My reviews</a></li>

View file

@ -0,0 +1,40 @@
{% extends "base.html" %}
{# Copyright The IETF Trust 2015, All Rights Reserved #}
{% load origin %}
{% load bootstrap3 %}
{% load staticfiles %}
{% block title %}Change username{% endblock %}
{% block content %}
{% origin %}
<div class="row">
<div class="col-md-2 col-sm-0"></div>
<div class="col-md-8 col-sm-12">
<h1>Change username</h1>
<div class="help-block">
This form lets you change your username (login) from {{ user.username }} to
one of your other active email addresses. If you want to change to a new
email address, then please first
<a href="{% url 'ietf.ietfauth.views.profile' %}">edit your profile</a>
to add that email address to the active email addresses for your account.
</div>
<form method="post">
{% csrf_token %}
{% bootstrap_form form %}
{% buttons %}
<button type="submit" class="btn btn-primary">Change username</button>
{% endbuttons %}
</form>
</div>
<div class="col-md-2 col-sm-0"></div>
</div>
{% endblock %}

View file

@ -0,0 +1,10 @@
{% autoescape off %}
Hello,
{% filter wordwrap:73 %}The username (login name) for your datatracker account was just changed using the username change form. If this was not done by you, please contact the secretariat at ietf-action@ietf.org for assistance.{% endfilter %}
Best regards,
The datatracker account manager service
(for the IETF Secretariat)
{% endautoescape %}