Merge account registration fixes branch
- Legacy-Id: 11169
This commit is contained in:
commit
793bc3c2fa
|
@ -1,235 +1,96 @@
|
|||
import datetime
|
||||
import hashlib
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
from django import forms
|
||||
from django.forms import ModelForm
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.sites.models import Site
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.html import mark_safe
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
|
||||
from ietf.utils.mail import send_mail
|
||||
from ietf.person.models import Person, Email, Alias
|
||||
from ietf.group.models import Role
|
||||
from ietf.person.models import Person, Email
|
||||
|
||||
|
||||
class RegistrationForm(forms.Form):
|
||||
|
||||
email = forms.EmailField(label="Your email (lowercase)")
|
||||
realm = 'IETF'
|
||||
expire = 3
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# why is there a save when it doesn't save?
|
||||
self.send_email()
|
||||
return True
|
||||
|
||||
def send_email(self):
|
||||
domain = Site.objects.get_current().domain
|
||||
subject = 'Confirm registration at %s' % domain
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
to_email = self.cleaned_data['email']
|
||||
today = datetime.date.today().strftime('%Y%m%d')
|
||||
auth = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, today, to_email, self.realm)).hexdigest()
|
||||
context = {
|
||||
'domain': domain,
|
||||
'today': today,
|
||||
'realm': self.realm,
|
||||
'auth': auth,
|
||||
'username': to_email,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
}
|
||||
send_mail(self.request, to_email, from_email, subject, 'registration/creation_email.txt', context)
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get('email', '')
|
||||
if not email:
|
||||
return email
|
||||
if email.lower() != email:
|
||||
raise forms.ValidationError(_('The supplied address contained uppercase letters. Please use a lowercase email address.'))
|
||||
if User.objects.filter(username=email).count():
|
||||
raise forms.ValidationError(_('An account with the email address you provided already exists.'))
|
||||
return email
|
||||
|
||||
|
||||
class RecoverPasswordForm(RegistrationForm):
|
||||
|
||||
realm = 'IETF'
|
||||
|
||||
def send_email(self):
|
||||
domain = Site.objects.get_current().domain
|
||||
subject = 'Password reset at %s' % domain
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
today = datetime.date.today().strftime('%Y%m%d')
|
||||
to_email = self.cleaned_data['email']
|
||||
today = datetime.date.today().strftime('%Y%m%d')
|
||||
auth = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, today, to_email, self.realm)).hexdigest()
|
||||
context = {
|
||||
'domain': domain,
|
||||
'today': today,
|
||||
'realm': self.realm,
|
||||
'auth': auth,
|
||||
'username': to_email,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
}
|
||||
send_mail(self.request, to_email, from_email, subject, 'registration/password_reset_email.txt', context)
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data.get('email', '')
|
||||
raise forms.ValidationError('The supplied address contained uppercase letters. Please use a lowercase email address.')
|
||||
if User.objects.filter(username=email).exists():
|
||||
raise forms.ValidationError('An account with the email address you provided already exists.')
|
||||
return email
|
||||
|
||||
|
||||
class PasswordForm(forms.Form):
|
||||
password = forms.CharField(widget=forms.PasswordInput)
|
||||
password_confirmation = forms.CharField(widget=forms.PasswordInput,
|
||||
help_text="Enter the same password as above, for verification.")
|
||||
|
||||
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
||||
password2 = forms.CharField(label=_("Password confirmation"), widget=forms.PasswordInput,
|
||||
help_text=_("Enter the same password as above, for verification."))
|
||||
def clean_password_confirmation(self):
|
||||
password = self.cleaned_data.get("password", "")
|
||||
password_confirmation = self.cleaned_data["password_confirmation"]
|
||||
if password != password_confirmation:
|
||||
raise forms.ValidationError("The two password fields didn't match.")
|
||||
return password_confirmation
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.username = kwargs.pop('username')
|
||||
self.update_user = User.objects.filter(username=self.username).count() > 0
|
||||
super(PasswordForm, self).__init__(*args, **kwargs)
|
||||
|
||||
def clean_password2(self):
|
||||
password1 = self.cleaned_data.get("password1", "")
|
||||
password2 = self.cleaned_data["password2"]
|
||||
if password1 != password2:
|
||||
raise forms.ValidationError(_("The two password fields didn't match."))
|
||||
return password2
|
||||
def ascii_cleaner(supposedly_ascii):
|
||||
outside_printable_ascii_pattern = r'[^\x20-\x7F]'
|
||||
if re.search(outside_printable_ascii_pattern, supposedly_ascii):
|
||||
raise forms.ValidationError("Please only enter ASCII characters.")
|
||||
return supposedly_ascii
|
||||
|
||||
def get_password(self):
|
||||
return self.cleaned_data.get('password1')
|
||||
class PersonForm(ModelForm):
|
||||
class Meta:
|
||||
model = Person
|
||||
exclude = ('time', 'user')
|
||||
|
||||
def create_user(self):
|
||||
user = User.objects.create(username=self.username,
|
||||
email=self.username)
|
||||
email = Email.objects.filter(address=self.username)
|
||||
person = None
|
||||
if email.count():
|
||||
email = email[0]
|
||||
if email.person:
|
||||
person = email.person
|
||||
else:
|
||||
email = None
|
||||
if not person:
|
||||
person = Person.objects.create(user=user,
|
||||
name=self.username,
|
||||
ascii=self.username)
|
||||
if not email:
|
||||
email = Email.objects.create(address=self.username,
|
||||
person=person)
|
||||
email.person = person
|
||||
email.save()
|
||||
person.user = user
|
||||
person.save()
|
||||
return user
|
||||
def clean_ascii(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii") or u"")
|
||||
|
||||
def get_user(self):
|
||||
return User.objects.get(username=self.username)
|
||||
def clean_ascii_short(self):
|
||||
return ascii_cleaner(self.cleaned_data.get("ascii_short") or u"")
|
||||
|
||||
def save_password_file(self):
|
||||
if getattr(settings, 'USE_PYTHON_HTDIGEST', None):
|
||||
pass_file = settings.HTPASSWD_FILE
|
||||
realm = settings.HTDIGEST_REALM
|
||||
password = self.get_password()
|
||||
username = self.username
|
||||
prefix = '%s:%s:' % (username, realm)
|
||||
key = hashlib.md5(prefix + password).hexdigest()
|
||||
f = open(pass_file, 'r+')
|
||||
pos = f.tell()
|
||||
line = f.readline()
|
||||
while line:
|
||||
if line.startswith(prefix):
|
||||
break
|
||||
pos=f.tell()
|
||||
line = f.readline()
|
||||
f.seek(pos)
|
||||
f.write('%s%s\n' % (prefix, key))
|
||||
f.close()
|
||||
else:
|
||||
p = subprocess.Popen([settings.HTPASSWD_COMMAND, "-b", settings.HTPASSWD_FILE, self.username, self.get_password()], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
||||
|
||||
def save(self):
|
||||
if self.update_user:
|
||||
user = self.get_user()
|
||||
else:
|
||||
user = self.create_user()
|
||||
user.set_password(self.get_password())
|
||||
user.save()
|
||||
self.save_password_file()
|
||||
return user
|
||||
class NewEmailForm(forms.Form):
|
||||
new_email = forms.EmailField(label="New email address", required=False)
|
||||
|
||||
def clean_new_email(self):
|
||||
email = self.cleaned_data.get("new_email", "")
|
||||
if email:
|
||||
existing = Email.objects.filter(address=email).first()
|
||||
if existing:
|
||||
raise forms.ValidationError("Email address '%s' is already assigned to account '%s' (%s)" % (existing, existing.person and existing.person.user, existing.person))
|
||||
return email
|
||||
|
||||
|
||||
class RoleEmailForm(forms.Form):
|
||||
email = forms.ModelChoiceField(label="Role email", queryset=Email.objects.all())
|
||||
|
||||
def __init__(self, role, *args, **kwargs):
|
||||
super(RoleEmailForm, self).__init__(*args, **kwargs)
|
||||
|
||||
f = self.fields["email"]
|
||||
f.label = u"%s in %s" % (role.name, role.group.acronym.upper())
|
||||
f.help_text = u"Email to use for <i>%s</i> role in %s" % (role.name, role.group.name)
|
||||
f.queryset = f.queryset.filter(models.Q(person=role.person_id) | models.Q(role=role))
|
||||
f.initial = role.email_id
|
||||
f.choices = [(e.pk, e.address if e.active else u"({})".format(e.address)) for e in f.queryset]
|
||||
|
||||
|
||||
class ResetPasswordForm(forms.Form):
|
||||
email = forms.EmailField(label="Your email (lowercase)")
|
||||
|
||||
def clean_email(self):
|
||||
email = self.cleaned_data["email"]
|
||||
if not User.objects.filter(username=email).exists():
|
||||
raise forms.ValidationError(mark_safe("Didn't find a matching account. If you don't have an account yet, you can <a href=\"{}\">create one</a>.".format(urlreverse("create_account"))))
|
||||
return email
|
||||
|
||||
|
||||
class TestEmailForm(forms.Form):
|
||||
email = forms.EmailField(required=False)
|
||||
|
||||
class PersonForm(ModelForm):
|
||||
request = None
|
||||
new_emails = []
|
||||
class Meta:
|
||||
model = Person
|
||||
exclude = ('time','user')
|
||||
|
||||
def confirm_address(self,email):
|
||||
person = self.instance
|
||||
domain = Site.objects.get_current().domain
|
||||
user = person.user
|
||||
if len(email) == 0:
|
||||
return
|
||||
subject = 'Confirm email address for %s' % person.name
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
to_email = email
|
||||
today = datetime.date.today().strftime('%Y%m%d')
|
||||
auth = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, today, to_email, user)).hexdigest()
|
||||
context = {
|
||||
'today': today,
|
||||
'domain': domain,
|
||||
'user': user,
|
||||
'email': email,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
'auth': auth,
|
||||
}
|
||||
send_mail(self.request, to_email, from_email, subject, 'registration/add_email_email.txt', context)
|
||||
|
||||
def save(self, force_insert=False, force_update=False, commit=True):
|
||||
m = super(PersonForm, self).save(commit=False)
|
||||
self.new_emails = [v for k,v in self.data.items() if k[:10] == u'new_email_' and u'@' in v]
|
||||
|
||||
for email in self.new_emails:
|
||||
self.confirm_address(email)
|
||||
|
||||
# Process email active flags
|
||||
emails = Email.objects.filter(person=self.instance)
|
||||
for email in emails:
|
||||
email.active = self.data.__contains__(email.address)
|
||||
if commit:
|
||||
email.save()
|
||||
|
||||
# Process email for roles
|
||||
for k,v in self.data.items():
|
||||
if k[:11] == u'role_email_':
|
||||
role = Role.objects.get(id=k[11:])
|
||||
email = Email.objects.get(address = v)
|
||||
role.email = email
|
||||
if commit:
|
||||
role.save()
|
||||
|
||||
# Make sure the alias table contains any new and/or old names.
|
||||
old_names = set([x.name for x in Alias.objects.filter(person=self.instance)])
|
||||
curr_names = set([x for x in [self.instance.name,
|
||||
self.instance.ascii,
|
||||
self.instance.ascii_short,
|
||||
self.data['name'],
|
||||
self.data['ascii'],
|
||||
self.data['ascii_short']] if len(x)])
|
||||
new_names = curr_names - old_names
|
||||
for name in new_names:
|
||||
alias = Alias(person=self.instance,name=name)
|
||||
if commit:
|
||||
alias.save()
|
||||
|
||||
if commit:
|
||||
m.save()
|
||||
return m
|
||||
|
||||
|
|
24
ietf/ietfauth/htpasswd.py
Normal file
24
ietf/ietfauth/htpasswd.py
Normal file
|
@ -0,0 +1,24 @@
|
|||
import subprocess, hashlib
|
||||
|
||||
from django.conf import settings
|
||||
|
||||
def save_htpasswd_file(username, password):
|
||||
if getattr(settings, 'USE_PYTHON_HTDIGEST', None):
|
||||
pass_file = settings.HTPASSWD_FILE
|
||||
realm = settings.HTDIGEST_REALM
|
||||
prefix = '%s:%s:' % (username, realm)
|
||||
key = hashlib.md5(prefix + password).hexdigest()
|
||||
f = open(pass_file, 'r+')
|
||||
pos = f.tell()
|
||||
line = f.readline()
|
||||
while line:
|
||||
if line.startswith(prefix):
|
||||
break
|
||||
pos=f.tell()
|
||||
line = f.readline()
|
||||
f.seek(pos)
|
||||
f.write('%s%s\n' % (prefix, key))
|
||||
f.close()
|
||||
else:
|
||||
p = subprocess.Popen([settings.HTPASSWD_COMMAND, "-b", settings.HTPASSWD_FILE, username, password], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
||||
stdout, stderr = p.communicate()
|
|
@ -1,83 +1,262 @@
|
|||
# Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
# All rights reserved. Contact: Pasi Eronen <pasi.eronen@nokia.com>
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
#
|
||||
# * Redistributions in binary form must reproduce the above
|
||||
# copyright notice, this list of conditions and the following
|
||||
# disclaimer in the documentation and/or other materials provided
|
||||
# with the distribution.
|
||||
#
|
||||
# * Neither the name of the Nokia Corporation and/or its
|
||||
# subsidiary(-ies) nor the names of its contributors may be used
|
||||
# to endorse or promote products derived from this software
|
||||
# without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
import os, shutil
|
||||
from urlparse import urlsplit
|
||||
|
||||
from pyquery import PyQuery
|
||||
|
||||
from django.core.urlresolvers import reverse as urlreverse
|
||||
from django.contrib.auth.models import User
|
||||
from django.conf import settings
|
||||
|
||||
from ietf.utils.test_utils import TestCase, login_testing_unauthorized, unicontent
|
||||
from ietf.utils.test_data import make_test_data
|
||||
from ietf.utils.mail import outbox, empty_outbox
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.group.models import Group, Role, RoleName
|
||||
|
||||
class IetfAuthTests(TestCase):
|
||||
def setUp(self):
|
||||
self.saved_htpasswd_file = settings.HTPASSWD_FILE
|
||||
self.htpasswd_dir = os.path.abspath("tmp-htpasswd-dir")
|
||||
os.mkdir(self.htpasswd_dir)
|
||||
settings.HTPASSWD_FILE = os.path.join(self.htpasswd_dir, "htpasswd")
|
||||
open(settings.HTPASSWD_FILE, 'a').close() # create empty file
|
||||
self.saved_htdigest_realm = getattr(settings, "HTDIGEST_REALM", None)
|
||||
settings.HTDIGEST_REALM = "test-realm"
|
||||
|
||||
def tearDown(self):
|
||||
shutil.rmtree(self.htpasswd_dir)
|
||||
settings.HTPASSWD_FILE = self.saved_htpasswd_file
|
||||
settings.HTDIGEST_REALM = self.saved_htdigest_realm
|
||||
|
||||
def test_index(self):
|
||||
self.assertEqual(self.client.get(urlreverse("ietf.ietfauth.views.index")).status_code, 200)
|
||||
|
||||
def test_login(self):
|
||||
def test_login_and_logout(self):
|
||||
make_test_data()
|
||||
|
||||
# try logging in without a next
|
||||
r = self.client.get('/accounts/login/')
|
||||
r = self.client.get(urlreverse("account_login"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
r = self.client.post('/accounts/login/', {"username":"plain", "password":"plain+password"})
|
||||
r = self.client.post(urlreverse("account_login"), {"username":"plain", "password":"plain+password"})
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(urlsplit(r["Location"])[2], "/accounts/profile/")
|
||||
|
||||
# try logging out
|
||||
r = self.client.get('/accounts/logout/')
|
||||
r = self.client.get(urlreverse("account_logout"))
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
r = self.client.get('/accounts/profile/')
|
||||
r = self.client.get(urlreverse("account_profile"))
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(urlsplit(r["Location"])[2], "/accounts/login/")
|
||||
|
||||
# try logging in with a next
|
||||
r = self.client.post('/accounts/login/?next=/foobar', {"username":"plain", "password":"plain+password"})
|
||||
r = self.client.post(urlreverse("account_login") + "?next=/foobar", {"username":"plain", "password":"plain+password"})
|
||||
self.assertEqual(r.status_code, 302)
|
||||
self.assertEqual(urlsplit(r["Location"])[2], "/foobar")
|
||||
|
||||
def extract_confirm_url(self, confirm_email):
|
||||
# dig out confirm_email link
|
||||
msg = confirm_email.get_payload(decode=True)
|
||||
line_start = "http"
|
||||
confirm_url = None
|
||||
for line in msg.split("\n"):
|
||||
if line.strip().startswith(line_start):
|
||||
confirm_url = line.strip()
|
||||
self.assertTrue(confirm_url)
|
||||
|
||||
return confirm_url
|
||||
|
||||
def username_in_htpasswd_file(self, username):
|
||||
with open(settings.HTPASSWD_FILE) as f:
|
||||
for l in f:
|
||||
if l.startswith(username + ":" + settings.HTDIGEST_REALM):
|
||||
return True
|
||||
return False
|
||||
|
||||
def test_create_account(self):
|
||||
make_test_data()
|
||||
|
||||
url = urlreverse('create_account')
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# register email
|
||||
email = 'new-account@example.com'
|
||||
empty_outbox()
|
||||
r = self.client.post(url, { 'email': email })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("Account created" in unicontent(r))
|
||||
self.assertEqual(len(outbox), 1)
|
||||
|
||||
# go to confirm page
|
||||
confirm_url = self.extract_confirm_url(outbox[-1])
|
||||
r = self.client.get(confirm_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# password mismatch
|
||||
r = self.client.post(confirm_url, { 'password': 'secret', 'password_confirmation': 'nosecret' })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(User.objects.filter(username=email).count(), 0)
|
||||
|
||||
# confirm
|
||||
r = self.client.post(confirm_url, { 'password': 'secret', 'password_confirmation': 'secret' })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(User.objects.filter(username=email).count(), 1)
|
||||
self.assertEqual(Person.objects.filter(user__username=email).count(), 1)
|
||||
self.assertEqual(Email.objects.filter(person__user__username=email).count(), 1)
|
||||
|
||||
self.assertTrue(self.username_in_htpasswd_file(email))
|
||||
|
||||
def test_profile(self):
|
||||
make_test_data()
|
||||
|
||||
url = urlreverse('ietf.ietfauth.views.profile')
|
||||
login_testing_unauthorized(self, "plain", url)
|
||||
username = "plain"
|
||||
email_address = Email.objects.filter(person__user__username=username).first().address
|
||||
|
||||
url = urlreverse('account_profile')
|
||||
login_testing_unauthorized(self, username, url)
|
||||
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('.form-control-static:contains("%s")' % username)), 1)
|
||||
self.assertEqual(len(q('[name="active_emails"][value="%s"][checked]' % email_address)), 1)
|
||||
|
||||
base_data = {
|
||||
"name": u"Test Nãme",
|
||||
"ascii": u"Test Name",
|
||||
"ascii_short": u"T. Name",
|
||||
"address": "Test address",
|
||||
"affiliation": "Test Org",
|
||||
"active_emails": email_address,
|
||||
}
|
||||
|
||||
# edit details - faulty ASCII
|
||||
faulty_ascii = base_data.copy()
|
||||
faulty_ascii["ascii"] = u"Test Nãme"
|
||||
r = self.client.post(url, faulty_ascii)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q("form .has-error")) > 0)
|
||||
|
||||
# edit details
|
||||
r = self.client.post(url, base_data)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
person = Person.objects.get(user__username=username)
|
||||
self.assertEqual(person.name, u"Test Nãme")
|
||||
self.assertEqual(person.ascii, u"Test Name")
|
||||
self.assertEqual(Person.objects.filter(alias__name=u"Test Name", user__username=username).count(), 1)
|
||||
self.assertEqual(Person.objects.filter(alias__name=u"Test Nãme", user__username=username).count(), 1)
|
||||
self.assertEqual(Email.objects.filter(address=email_address, person__user__username=username, active=True).count(), 1)
|
||||
|
||||
# deactivate address
|
||||
without_email_address = { k: v for k, v in base_data.iteritems() if k != "active_emails" }
|
||||
|
||||
r = self.client.post(url, without_email_address)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(Email.objects.filter(address=email_address, person__user__username="plain", active=True).count(), 0)
|
||||
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('[name="%s"][checked]' % email_address)), 0)
|
||||
|
||||
# add email address
|
||||
empty_outbox()
|
||||
new_email_address = "plain2@example.com"
|
||||
with_new_email_address = base_data.copy()
|
||||
with_new_email_address["new_email"] = new_email_address
|
||||
r = self.client.post(url, with_new_email_address)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(len(outbox), 1)
|
||||
|
||||
# confirm new email address
|
||||
confirm_url = self.extract_confirm_url(outbox[-1])
|
||||
r = self.client.get(confirm_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('[name="action"][value=confirm]')), 1)
|
||||
|
||||
r = self.client.post(confirm_url, { "action": "confirm" })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(Email.objects.filter(address=new_email_address, person__user__username=username, active=1).count(), 1)
|
||||
|
||||
# check that we can't re-add it - that would give a duplicate
|
||||
r = self.client.get(confirm_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('[name="action"][value="confirm"]')), 0)
|
||||
|
||||
# change role email
|
||||
role = Role.objects.create(
|
||||
person=Person.objects.get(user__username=username),
|
||||
email=Email.objects.get(address=email_address),
|
||||
name=RoleName.objects.get(slug="chair"),
|
||||
group=Group.objects.get(acronym="mars"),
|
||||
)
|
||||
|
||||
role_email_input_name = "role_%s-email" % role.pk
|
||||
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q('[name="%s"]' % role_email_input_name)), 1)
|
||||
|
||||
with_changed_role_email = base_data.copy()
|
||||
with_changed_role_email["active_emails"] = new_email_address
|
||||
with_changed_role_email[role_email_input_name] = new_email_address
|
||||
r = self.client.post(url, with_changed_role_email)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
updated_roles = Role.objects.filter(person=role.person, name=role.name, group=role.group)
|
||||
self.assertEqual(len(updated_roles), 1)
|
||||
self.assertEqual(updated_roles[0].email_id, new_email_address)
|
||||
|
||||
|
||||
def test_reset_password(self):
|
||||
url = urlreverse('password_reset')
|
||||
|
||||
user = User.objects.create(username="someone@example.com", email="someone@example.com")
|
||||
user.set_password("forgotten")
|
||||
user.save()
|
||||
p = Person.objects.create(name="Some One", ascii="Some One", user=user)
|
||||
Email.objects.create(address=user.username, person=p)
|
||||
|
||||
# get
|
||||
r = self.client.get(url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertTrue("plain" in unicontent(r))
|
||||
|
||||
# post
|
||||
# ... fill in
|
||||
# ask for reset, wrong username
|
||||
r = self.client.post(url, { 'email': "nobody@example.com" })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q("form .has-error")) > 0)
|
||||
|
||||
# we're missing tests of the other views
|
||||
# ask for reset
|
||||
empty_outbox()
|
||||
r = self.client.post(url, { 'email': user.username })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
self.assertEqual(len(outbox), 1)
|
||||
|
||||
# go to change password page
|
||||
confirm_url = self.extract_confirm_url(outbox[-1])
|
||||
r = self.client.get(confirm_url)
|
||||
self.assertEqual(r.status_code, 200)
|
||||
|
||||
# password mismatch
|
||||
r = self.client.post(confirm_url, { 'password': 'secret', 'password_confirmation': 'nosecret' })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertTrue(len(q("form .has-error")) > 0)
|
||||
|
||||
# confirm
|
||||
r = self.client.post(confirm_url, { 'password': 'secret', 'password_confirmation': 'secret' })
|
||||
self.assertEqual(r.status_code, 200)
|
||||
q = PyQuery(r.content)
|
||||
self.assertEqual(len(q("form .has-error")), 0)
|
||||
self.assertTrue(self.username_in_htpasswd_file(user.username))
|
||||
|
|
|
@ -6,17 +6,16 @@ from django.contrib.auth.views import login, logout
|
|||
urlpatterns = patterns('ietf.ietfauth.views',
|
||||
url(r'^$', 'index', name='account_index'),
|
||||
# url(r'^login/$', 'ietf_login'),
|
||||
url(r'^login/$', login),
|
||||
url(r'^logout/$', logout),
|
||||
url(r'^login/$', login, name="account_login"),
|
||||
url(r'^logout/$', logout, name="account_logout"),
|
||||
# url(r'^loggedin/$', 'ietf_loggedin'),
|
||||
# url(r'^loggedout/$', 'logged_out'),
|
||||
url(r'^profile/$', 'profile'),
|
||||
url(r'^profile/$', 'profile', name="account_profile"),
|
||||
# (r'^login/(?P<user>[a-z0-9.@]+)/(?P<passwd>.+)$', 'url_login'),
|
||||
url(r'^testemail/$', 'test_email'),
|
||||
url(r'^create/$', 'create_account', name='create_account'),
|
||||
url(r'^confirm/(?P<username>[\w.@+-]+)/(?P<date>[\d]+)/(?P<realm>[\w]+)/(?P<hash>[a-f0-9]+)/$', 'confirm_account', name='confirm_account'),
|
||||
url(r'^reset/$', 'password_reset_view', name='password_reset'),
|
||||
url(r'^reset/confirm/(?P<username>[\w.@+-]+)/(?P<date>[\d]+)/(?P<realm>[\w]+)/(?P<hash>[a-f0-9]+)/$', 'confirm_password_reset', name='confirm_password_reset'),
|
||||
url(r'^add_email/confirm/(?P<username>[\w.@+-]+)/(?P<date>[\d]+)/(?P<email>[\w.@+-]+)/(?P<hash>[a-f0-9]+)/$', 'confirm_new_email', name='confirm_new_email'),
|
||||
# url(r'^ajax/check_username/$', 'ajax_check_username', name='ajax_check_username'),
|
||||
url(r'^create/confirm/(?P<auth>[^/]+)/$', 'confirm_account', name='confirm_account'),
|
||||
url(r'^reset/$', 'password_reset', name='password_reset'),
|
||||
url(r'^reset/confirm/(?P<auth>[^/]+)/$', 'confirm_password_reset', name='confirm_password_reset'),
|
||||
url(r'^confirmnewemail/(?P<auth>[^/]+)/$', 'confirm_new_email', name='confirm_new_email'),
|
||||
)
|
||||
|
|
|
@ -138,4 +138,3 @@ def is_authorized_in_doc_stream(user, doc):
|
|||
group_req = Q()
|
||||
|
||||
return Role.objects.filter(Q(name__in=("chair", "secr", "delegate", "auth"), person__user=user) & group_req).exists()
|
||||
|
||||
|
|
|
@ -32,27 +32,25 @@
|
|||
|
||||
# Copyright The IETF Trust 2007, All Rights Reserved
|
||||
|
||||
import datetime
|
||||
import hashlib
|
||||
#import json
|
||||
|
||||
from django.conf import settings
|
||||
from django.template import RequestContext
|
||||
from django.http import Http404 #, HttpResponse, HttpResponseRedirect
|
||||
from django.shortcuts import render_to_response
|
||||
from django.shortcuts import render, redirect, get_object_or_404
|
||||
#from django.contrib.auth import REDIRECT_FIELD_NAME, authenticate, login
|
||||
from django.contrib.auth.decorators import login_required
|
||||
#from django.contrib.auth.models import User
|
||||
#from django.utils.http import urlquote
|
||||
#from django.utils.translation import ugettext as _
|
||||
from django.core.exceptions import ValidationError
|
||||
import django.core.signing
|
||||
from django.contrib.sites.models import Site
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from ietf.group.models import Role
|
||||
from ietf.ietfauth.forms import RegistrationForm, PasswordForm, RecoverPasswordForm, TestEmailForm, PersonForm
|
||||
from ietf.person.models import Person, Email
|
||||
from ietf.ietfauth.forms import RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm
|
||||
from ietf.ietfauth.forms import PersonForm, RoleEmailForm, NewEmailForm
|
||||
from ietf.ietfauth.htpasswd import save_htpasswd_file
|
||||
from ietf.person.models import Person, Email, Alias
|
||||
from ietf.utils.mail import send_mail
|
||||
|
||||
def index(request):
|
||||
return render_to_response('registration/index.html', context_instance=RequestContext(request))
|
||||
return render(request, 'registration/index.html')
|
||||
|
||||
# def url_login(request, user, passwd):
|
||||
# user = authenticate(username=user, password=passwd)
|
||||
|
@ -81,154 +79,256 @@ def index(request):
|
|||
# redirect_to = settings.LOGIN_REDIRECT_URL
|
||||
# return HttpResponseRedirect(redirect_to)
|
||||
|
||||
def create_account(request):
|
||||
to_email = None
|
||||
|
||||
if request.method == 'POST':
|
||||
form = RegistrationForm(request.POST)
|
||||
if form.is_valid():
|
||||
to_email = form.cleaned_data['email']
|
||||
|
||||
auth = django.core.signing.dumps(to_email, salt="create_account")
|
||||
|
||||
domain = Site.objects.get_current().domain
|
||||
subject = 'Confirm registration at %s' % domain
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
|
||||
send_mail(request, to_email, from_email, subject, 'registration/creation_email.txt', {
|
||||
'domain': domain,
|
||||
'auth': auth,
|
||||
'username': to_email,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
})
|
||||
else:
|
||||
form = RegistrationForm()
|
||||
|
||||
return render(request, 'registration/create.html', {
|
||||
'form': form,
|
||||
'to_email': to_email,
|
||||
})
|
||||
|
||||
def confirm_account(request, auth):
|
||||
try:
|
||||
email = django.core.signing.loads(auth, salt="create_account", max_age=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK * 24 * 60 * 60)
|
||||
except django.core.signing.BadSignature:
|
||||
raise Http404("Invalid or expired auth")
|
||||
|
||||
if User.objects.filter(username=email).exists():
|
||||
return redirect("account_profile")
|
||||
|
||||
success = False
|
||||
if request.method == 'POST':
|
||||
form = PasswordForm(request.POST)
|
||||
if form.is_valid():
|
||||
password = form.cleaned_data["password"]
|
||||
|
||||
user = User.objects.create(username=email, email=email)
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
# password is also stored in htpasswd file
|
||||
save_htpasswd_file(email, password)
|
||||
|
||||
# make sure the rest of the person infrastructure is
|
||||
# well-connected
|
||||
email_obj = Email.objects.filter(address=email).first()
|
||||
|
||||
person = None
|
||||
if email_obj and email_obj.person:
|
||||
person = email_obj.person
|
||||
|
||||
if not person:
|
||||
person = Person.objects.create(user=user,
|
||||
name=email,
|
||||
ascii=email)
|
||||
if not email_obj:
|
||||
email_obj = Email.objects.create(address=email, person=person)
|
||||
else:
|
||||
if not email_obj.person:
|
||||
email_obj.person = person
|
||||
email_obj.save()
|
||||
|
||||
person.user = user
|
||||
person.save()
|
||||
|
||||
success = True
|
||||
else:
|
||||
form = PasswordForm()
|
||||
|
||||
return render(request, 'registration/confirm_account.html', {
|
||||
'form': form,
|
||||
'email': email,
|
||||
'success': success,
|
||||
})
|
||||
|
||||
@login_required
|
||||
def profile(request):
|
||||
roles = []
|
||||
person = None
|
||||
|
||||
try:
|
||||
person = request.user.person
|
||||
except Person.DoesNotExist:
|
||||
return render_to_response('registration/missing_person.html', context_instance=RequestContext(request))
|
||||
return render(request, 'registration/missing_person.html')
|
||||
|
||||
roles = Role.objects.filter(person=person, group__state='active').order_by('name__name', 'group__name')
|
||||
emails = Email.objects.filter(person=person).order_by('-active','-time')
|
||||
new_email_forms = []
|
||||
|
||||
if request.method == 'POST':
|
||||
form = PersonForm(request.POST, instance=person)
|
||||
success = False
|
||||
new_emails = None
|
||||
error = None
|
||||
if form.is_valid():
|
||||
try:
|
||||
form.save()
|
||||
success = True
|
||||
new_emails = form.new_emails
|
||||
except Exception as e:
|
||||
error = e
|
||||
|
||||
return render_to_response('registration/confirm_profile_update.html',
|
||||
{ 'success': success, 'new_emails': new_emails, 'error': error} ,
|
||||
context_instance=RequestContext(request))
|
||||
person_form = PersonForm(request.POST, instance=person)
|
||||
for r in roles:
|
||||
r.email_form = RoleEmailForm(r, request.POST, prefix="role_%s" % r.pk)
|
||||
|
||||
for e in request.POST.getlist("new_email", []):
|
||||
new_email_forms.append(NewEmailForm({ "new_email": e }))
|
||||
|
||||
forms_valid = [person_form.is_valid()] + [r.email_form.is_valid() for r in roles] + [f.is_valid() for f in new_email_forms]
|
||||
|
||||
email_confirmations = []
|
||||
|
||||
if all(forms_valid):
|
||||
updated_person = person_form.save()
|
||||
|
||||
for f in new_email_forms:
|
||||
to_email = f.cleaned_data["new_email"]
|
||||
if not to_email:
|
||||
continue
|
||||
|
||||
email_confirmations.append(to_email)
|
||||
|
||||
auth = django.core.signing.dumps([person.user.username, to_email], salt="add_email")
|
||||
|
||||
domain = Site.objects.get_current().domain
|
||||
subject = u'Confirm email address for %s' % person.name
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
|
||||
send_mail(request, to_email, from_email, subject, 'registration/add_email_email.txt', {
|
||||
'domain': domain,
|
||||
'auth': auth,
|
||||
'email': to_email,
|
||||
'person': person,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
})
|
||||
|
||||
|
||||
for r in roles:
|
||||
e = r.email_form.cleaned_data["email"]
|
||||
if r.email_id != e.pk:
|
||||
r.email = e
|
||||
r.save()
|
||||
|
||||
active_emails = request.POST.getlist("active_emails", [])
|
||||
for email in emails:
|
||||
email.active = email.pk in active_emails
|
||||
email.save()
|
||||
|
||||
# Make sure the alias table contains any new and/or old names.
|
||||
existing_aliases = set(Alias.objects.filter(person=person).values_list("name", flat=True))
|
||||
curr_names = set(x for x in [updated_person.name, updated_person.ascii, updated_person.ascii_short] if x)
|
||||
new_aliases = curr_names - existing_aliases
|
||||
for name in new_aliases:
|
||||
Alias.objects.create(person=updated_person, name=name)
|
||||
|
||||
return render(request, 'registration/confirm_profile_update.html', {
|
||||
'email_confirmations': email_confirmations,
|
||||
})
|
||||
else:
|
||||
roles = Role.objects.filter(person=person,group__state='active').order_by('name__name','group__name')
|
||||
emails = Email.objects.filter(person=person).order_by('-active','-time')
|
||||
for r in roles:
|
||||
r.email_form = RoleEmailForm(r, prefix="role_%s" % r.pk)
|
||||
|
||||
person_form = PersonForm(instance=person)
|
||||
|
||||
return render_to_response('registration/edit_profile.html',
|
||||
{ 'user': request.user, 'emails': emails, 'person': person,
|
||||
'roles': roles, 'person_form': person_form } ,
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
def confirm_new_email(request, username, date, email, hash):
|
||||
valid = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, date, email, username)).hexdigest() == hash
|
||||
if not valid:
|
||||
raise Http404
|
||||
request_date = datetime.date(int(date[:4]), int(date[4:6]), int(date[6:]))
|
||||
if datetime.date.today() > (request_date + datetime.timedelta(days=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK)):
|
||||
raise Http404
|
||||
success = False
|
||||
|
||||
person = None
|
||||
error = None
|
||||
new_email = None
|
||||
return render(request, 'registration/edit_profile.html', {
|
||||
'user': request.user,
|
||||
'person': person,
|
||||
'person_form': person_form,
|
||||
'roles': roles,
|
||||
'emails': emails,
|
||||
'new_email_forms': new_email_forms,
|
||||
})
|
||||
|
||||
def confirm_new_email(request, auth):
|
||||
try:
|
||||
# First, check whether this address exists (to give a more sensible
|
||||
# error when a duplicate is created).
|
||||
existing_email = Email.objects.get(address=email)
|
||||
print existing_email
|
||||
existing_person = existing_email.person
|
||||
print existing_person
|
||||
error = {'address': ["Email address '%s' is already assigned to user '%s' (%s)" %
|
||||
(email, existing_person.user, existing_person.name)]}
|
||||
except Exception:
|
||||
try:
|
||||
person = Person.objects.get(user__username=username)
|
||||
new_email = Email(address=email, person=person, active=True, time=datetime.datetime.now())
|
||||
new_email.full_clean()
|
||||
new_email.save()
|
||||
success = True
|
||||
except Person.DoesNotExist:
|
||||
error = {'person': ["No such user: %s" % (username)]}
|
||||
except ValidationError as e:
|
||||
error = e.message_dict
|
||||
username, email = django.core.signing.loads(auth, salt="add_email", max_age=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK * 24 * 60 * 60)
|
||||
except django.core.signing.BadSignature:
|
||||
raise Http404("Invalid or expired auth")
|
||||
|
||||
return render_to_response('registration/confirm_new_email.html',
|
||||
{ 'username': username, 'email': email,
|
||||
'success': success, 'error': error,
|
||||
'record': new_email},
|
||||
context_instance=RequestContext(request))
|
||||
person = get_object_or_404(Person, user__username=username)
|
||||
|
||||
# do another round of validation since the situation may have
|
||||
# changed since submitting the request
|
||||
form = NewEmailForm({ "new_email": email })
|
||||
can_confirm = form.is_valid() and email
|
||||
new_email_obj = None
|
||||
if request.method == 'POST' and can_confirm and request.POST.get("action") == "confirm":
|
||||
new_email_obj = Email.objects.create(address=email, person=person)
|
||||
|
||||
def create_account(request):
|
||||
return render(request, 'registration/confirm_new_email.html', {
|
||||
'username': username,
|
||||
'email': email,
|
||||
'can_confirm': can_confirm,
|
||||
'form': form,
|
||||
'new_email_obj': new_email_obj,
|
||||
})
|
||||
|
||||
def password_reset(request):
|
||||
success = False
|
||||
if request.method == 'POST':
|
||||
form = RegistrationForm(request.POST)
|
||||
form = ResetPasswordForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.request = request
|
||||
form.save()
|
||||
to_email = form.cleaned_data['email']
|
||||
|
||||
auth = django.core.signing.dumps(to_email, salt="password_reset")
|
||||
|
||||
domain = Site.objects.get_current().domain
|
||||
subject = 'Confirm password reset at %s' % domain
|
||||
from_email = settings.DEFAULT_FROM_EMAIL
|
||||
|
||||
send_mail(request, to_email, from_email, subject, 'registration/password_reset_email.txt', {
|
||||
'domain': domain,
|
||||
'auth': auth,
|
||||
'username': to_email,
|
||||
'expire': settings.DAYS_TO_EXPIRE_REGISTRATION_LINK,
|
||||
})
|
||||
|
||||
success = True
|
||||
else:
|
||||
form = RegistrationForm()
|
||||
return render_to_response('registration/create.html',
|
||||
{'form': form,
|
||||
'success': success},
|
||||
context_instance=RequestContext(request))
|
||||
form = ResetPasswordForm()
|
||||
return render(request, 'registration/password_reset.html', {
|
||||
'form': form,
|
||||
'success': success,
|
||||
})
|
||||
|
||||
|
||||
def process_confirmation(request, username, date, realm, hash):
|
||||
valid = hashlib.md5('%s%s%s%s' % (settings.SECRET_KEY, date, username, realm)).hexdigest() == hash
|
||||
if not valid:
|
||||
raise Http404
|
||||
request_date = datetime.date(int(date[:4]), int(date[4:6]), int(date[6:]))
|
||||
if datetime.date.today() > (request_date + datetime.timedelta(days=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK)):
|
||||
raise Http404
|
||||
def confirm_password_reset(request, auth):
|
||||
try:
|
||||
email = django.core.signing.loads(auth, salt="password_reset", max_age=settings.DAYS_TO_EXPIRE_REGISTRATION_LINK * 24 * 60 * 60)
|
||||
except django.core.signing.BadSignature:
|
||||
raise Http404("Invalid or expired auth")
|
||||
|
||||
user = get_object_or_404(User, username=email)
|
||||
|
||||
success = False
|
||||
if request.method == 'POST':
|
||||
form = PasswordForm(request.POST, username=username)
|
||||
form = PasswordForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.save() # Also updates the httpd password file
|
||||
password = form.cleaned_data["password"]
|
||||
|
||||
user.set_password(password)
|
||||
user.save()
|
||||
# password is also stored in htpasswd file
|
||||
save_htpasswd_file(user.username, password)
|
||||
|
||||
success = True
|
||||
else:
|
||||
form = PasswordForm(username=username)
|
||||
return form, username, success
|
||||
form = PasswordForm()
|
||||
|
||||
def confirm_account(request, username, date, realm, hash):
|
||||
form, username, success = process_confirmation(request, username, date, realm, hash)
|
||||
return render_to_response('registration/confirm.html',
|
||||
{'form': form, 'email': username, 'success': success},
|
||||
context_instance=RequestContext(request))
|
||||
return render(request, 'registration/change_password.html', {
|
||||
'form': form,
|
||||
'email': email,
|
||||
'success': success,
|
||||
})
|
||||
|
||||
|
||||
def password_reset_view(request):
|
||||
success = False
|
||||
if request.method == 'POST':
|
||||
form = RecoverPasswordForm(request.POST)
|
||||
if form.is_valid():
|
||||
form.request = request
|
||||
form.save()
|
||||
success = True
|
||||
else:
|
||||
form = RecoverPasswordForm()
|
||||
return render_to_response('registration/password_reset.html',
|
||||
{'form': form,
|
||||
'success': success},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
|
||||
def confirm_password_reset(request, username, date, realm, hash):
|
||||
form, username, success = process_confirmation(request, username, date, realm, hash)
|
||||
return render_to_response('registration/change_password.html',
|
||||
{'form': form,
|
||||
'success': success,
|
||||
'username': username},
|
||||
context_instance=RequestContext(request))
|
||||
|
||||
# def ajax_check_username(request):
|
||||
# username = request.GET.get('username', '')
|
||||
# error = False
|
||||
# if User.objects.filter(username=username).count():
|
||||
# error = _('This email address is already registered')
|
||||
# return HttpResponse(json.dumps({'error': error}), content_type='text/plain')
|
||||
|
||||
def test_email(request):
|
||||
"""Set email address to which email generated in the system will be sent."""
|
||||
if settings.SERVER_MODE == "production":
|
||||
|
@ -249,11 +349,10 @@ def test_email(request):
|
|||
else:
|
||||
form = TestEmailForm(initial=dict(email=request.COOKIES.get('testmailcc')))
|
||||
|
||||
r = render_to_response('ietfauth/testemail.html',
|
||||
dict(form=form,
|
||||
cookie=cookie if cookie != None else request.COOKIES.get("testmailcc", "")
|
||||
),
|
||||
context_instance=RequestContext(request))
|
||||
r = render(request, 'ietfauth/testemail.html', {
|
||||
"form": form,
|
||||
"cookie": cookie if cookie != None else request.COOKIES.get("testmailcc", "")
|
||||
})
|
||||
|
||||
if cookie != None:
|
||||
r.set_cookie("testmailcc", cookie)
|
||||
|
|
|
@ -14,12 +14,15 @@ from ietf.utils.mail import send_mail_preformatted
|
|||
|
||||
class PersonInfo(models.Model):
|
||||
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
|
||||
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True) # The normal unicode form of the name. This must be
|
||||
# set to the same value as the ascii-form if equal.
|
||||
ascii = models.CharField("Full Name (ASCII)", max_length=255) # The normal ascii-form of the name.
|
||||
ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True) # The short ascii-form of the name. Also in alias table if non-null
|
||||
address = models.TextField(max_length=255, blank=True)
|
||||
affiliation = models.CharField(max_length=255, blank=True)
|
||||
# The normal unicode form of the name. This must be
|
||||
# set to the same value as the ascii-form if equal.
|
||||
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True, help_text="Preferred form of name.")
|
||||
# The normal ascii-form of the name.
|
||||
ascii = models.CharField("Full Name (ASCII)", max_length=255, help_text="Name as rendered in ASCII (Latin, unaccented) characters.")
|
||||
# The short ascii-form of the name. Also in alias table if non-null
|
||||
ascii_short = models.CharField("Abbreviated Name (ASCII)", max_length=32, null=True, blank=True, help_text="Example: A. Nonymous. Fill in this with initials and surname only if taking the initials and surname of the ASCII name above produces an incorrect initials-only form. (Blank is OK).")
|
||||
affiliation = models.CharField(max_length=255, blank=True, help_text="Employer, university, sponsor, etc.")
|
||||
address = models.TextField(max_length=255, blank=True, help_text="Postal mailing address.")
|
||||
|
||||
def __unicode__(self):
|
||||
return self.plain_name()
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
{% autoescape off %}
|
||||
Hello,
|
||||
|
||||
We have received a request to add the email address '{{ email }}'
|
||||
to the user account '{{ user }}' at '{{ domain }}'.
|
||||
If you requested this change, please confirm that this is your email
|
||||
address by clicking on following link:
|
||||
{% filter wordwrap:73 %}We have received a request to add the email address {{ email }} to the user account '{{ person.user }}' at {{ domain }}. If you requested this change, please confirm that this is your email address by clicking on following link:{% endfilter %}
|
||||
|
||||
https://{{ domain }}{% url "confirm_new_email" user today email auth %}
|
||||
https://{{ domain }}{% url "confirm_new_email" auth %}
|
||||
|
||||
This link will expire in {{ expire }} days.
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
{% else %}
|
||||
<h1>Change password</h1>
|
||||
|
||||
<p>You can change the password below for your user {{ username }} below.</p>
|
||||
<p>You can change the password below for your user {{ email }} below.</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
||||
|
|
|
@ -12,13 +12,13 @@
|
|||
{% if success %}
|
||||
<h1>Account creation successful</h1>
|
||||
|
||||
<p>Your account with login name {{ email }} has been created, using the password you have selected.</p>
|
||||
<a type="a" class="btn btn-primary" href="/accounts/login/" rel="nofollow">Sign in</a>
|
||||
<p>Your account with login {{ email }} has been created, using the password you have selected.</p>
|
||||
<a type="a" class="btn btn-primary" href="{% url "account_login" %}" rel="nofollow">Sign in</a>
|
||||
|
||||
{% else %}
|
||||
<h1>Complete account creation</h1>
|
||||
|
||||
<p>In order to complete the setup of your account with login name {{ email }}, please choose a password:</p>
|
||||
<p>In order to complete the setup of your account with login {{ email }}, please choose a password:</p>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% bootstrap_form form %}
|
|
@ -1,6 +1,6 @@
|
|||
{% extends "base.html" %}
|
||||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
{% load origin bootstrap3 %}
|
||||
|
||||
{% block title %}Confirm new email address{% endblock %}
|
||||
|
||||
|
@ -8,17 +8,28 @@
|
|||
{% origin %}
|
||||
<h1>Confirm new email address</h1>
|
||||
|
||||
{% if success %}
|
||||
<p>Your account with login name {{ username }} has been updated to include the email address {{ email }}.</p>
|
||||
<a class="btn btn-default" href="{% url "ietf.ietfauth.views.profile" %}">Edit profile</a>
|
||||
{% if not can_confirm %}
|
||||
<p class="alert alert-danger">An error has occured when attempting to add the email address {{ email }} to your account {{ username }}.</p>
|
||||
|
||||
{% bootstrap_form_errors form %}
|
||||
|
||||
<p>
|
||||
<a class="btn btn-default" href="{% url "account_profile" %}">Edit profile</a>
|
||||
</p>
|
||||
{% elif new_email_obj %}
|
||||
<p>Your account {{ username }} has been updated to include the email address {{ email }}.</p>
|
||||
|
||||
<p>
|
||||
<a class="btn btn-default" href="{% url "account_profile" %}">Edit profile</a>
|
||||
</p>
|
||||
{% else %}
|
||||
<p class="alert alert-danger">An error has occured when attempting to add the email address '{{ email }}' to your account '{{ username }}'.<p>
|
||||
<ul>
|
||||
{% for field,msgs in error.items %}
|
||||
{% for msg in msgs %}
|
||||
<li><b>{{field}}</b>: {{msg}}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
</ul>
|
||||
<p>Confirm that you want to add the email address {{ email }} to your account {{ username }}.</p>
|
||||
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{% buttons %}
|
||||
<button type="submit" class="btn btn-warning" name="action" value="confirm">Confirm email address</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,23 +2,18 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
|
||||
{% block title %}Profile update{% endblock %}
|
||||
{% block title %}Profile update successful{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
{% origin %}
|
||||
{% if success %}
|
||||
<h1>Profile update successful</h1>
|
||||
|
||||
<p>Your account has been successfully updated to reflect the changes you submitted.</p>
|
||||
{% for email in new_emails %}
|
||||
<p class="alert alert-info"><b>A confirmation email has been sent to {{email}}.</b> The email will be activated after you click on the link it contains.</p>
|
||||
{% endfor %}
|
||||
<a class="btn btn-default" href="{% url "ietf.ietfauth.views.profile" %}">Edit profile</a>
|
||||
{% else %}
|
||||
<h1>Profile update unsuccessful</h1>
|
||||
<p >An error has occurred when attempting to update your account.<p>
|
||||
{% if error %}
|
||||
<p class="alert alert-danger">{{ error }}</p>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
<h1>Profile update successful</h1>
|
||||
|
||||
<p>Your account has been updated to reflect the changes you submitted.</p>
|
||||
|
||||
{% for email in email_confirmations %}
|
||||
<p class="alert alert-info"><b>A confirmation email has been sent to {{email}}.</b> The email will be activated after you click on the link it contains.</p>
|
||||
{% endfor %}
|
||||
|
||||
<a class="btn btn-default" href="{% url "ietf.ietfauth.views.profile" %}">Edit profile</a>
|
||||
{% endblock %}
|
||||
|
|
|
@ -9,10 +9,12 @@
|
|||
{% block content %}
|
||||
{% origin %}
|
||||
|
||||
{% if success %}
|
||||
{% if to_email %}
|
||||
<h1>Account created successfully</h1>
|
||||
|
||||
<p>Your account creation request has been successfully received.</p>
|
||||
<p>We have sent you an email with instructions on how to complete the process.</p>
|
||||
|
||||
<p>We have sent an email to {{ to_email }} with instructions on how to complete the process.</p>
|
||||
|
||||
{% else %}
|
||||
<div class="row">
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
{% autoescape off %}
|
||||
Hello,
|
||||
|
||||
We have received an account creation request for {{ username }}
|
||||
at {{ domain }}. In order to set a new password for the
|
||||
{{ username }} account, please go to the following link and
|
||||
follow the instructions there:
|
||||
{% filter wordwrap:73 %}We have received an account creation request for {{ username }} at {{ domain }}. In order to set a new password for the {{ username }} account, please go to the following link and follow the instructions there:{% endfilter %}
|
||||
|
||||
https://{{ domain }}{% url "confirm_account" username today realm auth %}
|
||||
https://{{ domain }}{% url "confirm_account" auth %}
|
||||
|
||||
This link will expire in {{ expire }} days.
|
||||
|
||||
|
@ -14,4 +11,4 @@ Best regards,
|
|||
|
||||
The datatracker login manager service
|
||||
(for the IETF Secretariat)
|
||||
{% endautoescape %}
|
||||
{% endautoescape %}
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
{# Copyright The IETF Trust 2015, All Rights Reserved #}
|
||||
{% load origin %}
|
||||
|
||||
{% load widget_tweaks %}
|
||||
{% load widget_tweaks bootstrap3 %}
|
||||
|
||||
{% block title %}Profile for {{ user }}{% endblock %}
|
||||
|
||||
|
@ -13,6 +13,8 @@
|
|||
<form class="form-horizontal" method="post">
|
||||
{% csrf_token %}
|
||||
|
||||
{% bootstrap_form_errors person_form 'non_fields' %}
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">User name</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -20,32 +22,6 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Roles</label>
|
||||
<div class="col-sm-10">
|
||||
{% for role in roles %}
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
<select class="form-control" name="role_email_{{role.id}}">
|
||||
{% for email in emails %}
|
||||
<option value="{{email.address}}" {% if email.address == role.email.address %}selected{% endif %}>
|
||||
{% if email.active %}
|
||||
{{email}}
|
||||
{% else %}
|
||||
({{email}})
|
||||
{% endif %}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="help-block">Email to use for <i>{{ role.name|lower }}</i> role in {{ role.group.acronym|upper }} ({{ role.group.type }}).</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Email addresses</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -54,8 +30,8 @@
|
|||
{% for email in emails %}
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" name="{{email.address}}" {% if email.active %}checked{% endif %} onchange="style_email(0, this)">
|
||||
{{email}}
|
||||
<input type="checkbox" name="active_emails" value="{{ email.pk }}" {% if email.active %}checked{% endif %}>
|
||||
{{ email }}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
@ -67,85 +43,23 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{% for f in new_email_forms %}
|
||||
{% bootstrap_field f.new_email layout="horizontal" show_label=False %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
<button class="btn btn-default btn-sm" onclick="add_email(); return false">Add email address</button>
|
||||
<div class="new-emails"></div>
|
||||
|
||||
<button class="btn btn-default btn-sm add-email">Add email address</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{person_form.name.label}}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{person_form.name|add_class:"form-control"}}
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="help-block">The preferred form of your name.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% for role in roles %}
|
||||
{% bootstrap_field role.email_form.email layout="horizontal" show_label=False %}
|
||||
{% endfor %}
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{person_form.ascii.label}}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{person_form.ascii|add_class:"form-control"}}
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="help-block">Your name as rendered in ASCII (Latin, unaccented) characters.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{person_form.ascii_short.label}}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{person_form.ascii_short|add_class:"form-control"}}
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<span class="help-block">
|
||||
Example: A. Nonymous. Fill in this with initials and surname only if
|
||||
taking the initials and surname of your ASCII name above produces an incorrect
|
||||
initials-only form. (Blank is ok).
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">{{person_form.affiliation.label}}</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{person_form.affiliation|add_class:"form-control"}}
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="help-block">Employer, university, sponsor, etc.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label class="col-sm-2 control-label">Postal address</label>
|
||||
<div class="col-sm-10">
|
||||
<div class="row">
|
||||
<div class="col-sm-5">
|
||||
{{person_form.address|add_class:"form-control"}}
|
||||
</div>
|
||||
<div class="col-sm-7">
|
||||
<div class="help-block">Postal mailing address.</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% bootstrap_form person_form layout="horizontal" %}
|
||||
|
||||
<div class="form-group">
|
||||
<div class="col-sm-offset-2 col-sm-10">
|
||||
|
@ -158,23 +72,24 @@
|
|||
{% block js %}
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
$("input[type=checkbox]").each(style_email);
|
||||
$("input[name=active_emails]").on("change keypress click", function () {
|
||||
if (this.checked) {
|
||||
$(this).parent().addClass("text-success");;
|
||||
$(this).parent().removeClass("text-danger line-through");
|
||||
} else {
|
||||
$(this).parent().addClass("text-danger line-through");
|
||||
$(this).parent().removeClass("text-success");
|
||||
}
|
||||
}).trigger("change");
|
||||
|
||||
$(".add-email").click(function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var container = $(this).closest("form").find(".new-emails");
|
||||
|
||||
$('<input class="form-control" name="new_email" placeholder="Enter new email address...">').appendTo(container).focus();
|
||||
})
|
||||
});
|
||||
|
||||
function style_email(i, e) {
|
||||
if (e.checked) {
|
||||
$(e).parent().addClass("text-success");;
|
||||
$(e).parent().removeClass("text-danger line-through");
|
||||
} else {
|
||||
$(e).parent().addClass("text-danger line-through");
|
||||
$(e).parent().removeClass("text-success");
|
||||
}
|
||||
}
|
||||
|
||||
function add_email() {
|
||||
$("#emails").append('<input type="email" class="form-control" name="new_email_' + $.now() + '" placeholder="Enter new email address...">');
|
||||
$("#emails").children().last().focus();
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
|
|
@ -27,6 +27,5 @@
|
|||
<button type="submit" class="btn btn-warning">Reset password</button>
|
||||
{% endbuttons %}
|
||||
</form>
|
||||
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,12 +1,9 @@
|
|||
{% autoescape off %}
|
||||
Hello,
|
||||
|
||||
We have received a password reset request for {{ username }}
|
||||
at {{ domain }}. In order to set a new password for the
|
||||
{{ username }} account, please go to the following link and
|
||||
follow the instructions there:
|
||||
{% filter wordwrap:73 %}We have received a password reset request for {{ username }} at {{ domain }}. In order to set a new password for the {{ username }} account, please go to the following link and follow the instructions there:{% endfilter %}
|
||||
|
||||
https://{{ domain }}{% url "confirm_password_reset" username today realm auth %}
|
||||
https://{{ domain }}{% url "confirm_password_reset" auth %}
|
||||
|
||||
This link will expire in {{ expire }} days.
|
||||
|
||||
|
@ -17,4 +14,4 @@ Best regards,
|
|||
|
||||
The datatracker login manager service
|
||||
(for the IETF Secretariat)
|
||||
{% endautoescape %}
|
||||
{% endautoescape %}
|
||||
|
|
Loading…
Reference in a new issue