From fed2a04445bcc545f5899eb42113a578a0e0085c Mon Sep 17 00:00:00 2001
From: Henrik Levkowetz
Date: Tue, 14 Jun 2016 21:39:12 +0000
Subject: [PATCH] Increased the requirements for account creation, and added a
form where the secretariat can add whitelisting to make account creation
possible for addresses that don't fulfil the default requirements. -
Legacy-Id: 11374
---
ietf/ietfauth/forms.py | 7 ++
ietf/ietfauth/tests.py | 56 ++++++++++++-
ietf/ietfauth/urls.py | 3 +
ietf/ietfauth/views.py | 56 ++++++++++---
ietf/templates/ietfauth/whitelist_form.html | 93 +++++++++++++++++++++
ietf/templates/registration/manual.html | 21 +++++
6 files changed, 219 insertions(+), 17 deletions(-)
create mode 100644 ietf/templates/ietfauth/whitelist_form.html
create mode 100644 ietf/templates/registration/manual.html
diff --git a/ietf/ietfauth/forms.py b/ietf/ietfauth/forms.py
index 348a0f582..048fcdf50 100644
--- a/ietf/ietfauth/forms.py
+++ b/ietf/ietfauth/forms.py
@@ -12,6 +12,7 @@ from django.core.urlresolvers import reverse as urlreverse
import debug # pyflakes:ignore
from ietf.person.models import Person, Email
+from ietf.mailinglists.models import Whitelisted
class RegistrationForm(forms.Form):
@@ -118,3 +119,9 @@ class ResetPasswordForm(forms.Form):
class TestEmailForm(forms.Form):
email = forms.EmailField(required=False)
+class WhitelistForm(ModelForm):
+ class Meta:
+ model = Whitelisted
+ exclude = ['by', 'time' ]
+
+
diff --git a/ietf/ietfauth/tests.py b/ietf/ietfauth/tests.py
index 3d7722b0c..35c57a13a 100644
--- a/ietf/ietfauth/tests.py
+++ b/ietf/ietfauth/tests.py
@@ -1,5 +1,7 @@
# -*- coding: utf-8 -*-
-import os, shutil
+from __future__ import unicode_literals
+
+import os, shutil, time
from urlparse import urlsplit
from pyquery import PyQuery
from unittest import skipIf
@@ -15,6 +17,8 @@ from ietf.utils.mail import outbox, empty_outbox
from ietf.person.models import Person, Email
from ietf.group.models import Group, Role, RoleName
from ietf.ietfauth.htpasswd import update_htpasswd_file
+from ietf.mailinglists.models import Subscribed
+
import ietf.ietfauth.views
if os.path.exists(settings.HTPASSWD_COMMAND):
@@ -94,7 +98,7 @@ class IetfAuthTests(TestCase):
return False
- def test_create_account(self):
+ def test_create_account_failure(self):
make_test_data()
url = urlreverse(ietf.ietfauth.views.create_account)
@@ -103,12 +107,21 @@ class IetfAuthTests(TestCase):
r = self.client.get(url)
self.assertEqual(r.status_code, 200)
- # register email
+ # register email and verify failure
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.assertIn("Account creation failed", unicontent(r))
+
+ def register_and_verify(self, email):
+ url = urlreverse(ietf.ietfauth.views.create_account)
+
+ # register email
+ empty_outbox()
+ r = self.client.post(url, { 'email': email })
+ self.assertEqual(r.status_code, 200)
+ self.assertIn("Account created", unicontent(r))
self.assertEqual(len(outbox), 1)
# go to confirm page
@@ -130,6 +143,41 @@ class IetfAuthTests(TestCase):
self.assertTrue(self.username_in_htpasswd_file(email))
+ def test_create_whitelisted_account(self):
+ email = "new-account@example.com"
+
+ # add whitelist entry
+ r = self.client.post(urlreverse(django.contrib.auth.views.login), {"username":"secretary", "password":"secretary+password"})
+ self.assertEqual(r.status_code, 302)
+ self.assertEqual(urlsplit(r["Location"])[2], urlreverse(ietf.ietfauth.views.profile))
+
+ r = self.client.get(urlreverse(ietf.ietfauth.views.add_account_whitelist))
+ self.assertEqual(r.status_code, 200)
+ self.assertIn("Add a whitelist entry", unicontent(r))
+
+ r = self.client.post(urlreverse(ietf.ietfauth.views.add_account_whitelist), {"address": email})
+ self.assertEqual(r.status_code, 200)
+ self.assertIn("Whitelist entry creation successful", unicontent(r))
+
+ # log out
+ r = self.client.get(urlreverse(django.contrib.auth.views.logout))
+ self.assertEqual(r.status_code, 200)
+
+ # register and verify whitelisted email
+ self.register_and_verify(email)
+
+
+ def test_create_subscribed_account(self):
+ # verify creation with email in subscribed list
+ saved_delay = settings.LIST_ACCOUNT_DELAY
+ settings.LIST_ACCOUNT_DELAY = 1
+ email = "subscribed@example.com"
+ s = Subscribed(address=email)
+ s.save()
+ time.sleep(1.1)
+ self.register_and_verify(email)
+ settings.LIST_ACCOUNT_DELAY = saved_delay
+
def test_profile(self):
make_test_data()
diff --git a/ietf/ietfauth/urls.py b/ietf/ietfauth/urls.py
index 4d68db944..e7c4f0ed0 100644
--- a/ietf/ietfauth/urls.py
+++ b/ietf/ietfauth/urls.py
@@ -3,6 +3,8 @@
from django.conf.urls import patterns, url
from django.contrib.auth.views import login, logout
+from ietf.ietfauth.views import add_account_whitelist
+
urlpatterns = patterns('ietf.ietfauth.views',
url(r'^$', 'index'),
# url(r'^login/$', 'ietf_login'),
@@ -18,4 +20,5 @@ urlpatterns = patterns('ietf.ietfauth.views',
url(r'^reset/$', 'password_reset'),
url(r'^reset/confirm/(?P[^/]+)/$', 'confirm_password_reset'),
url(r'^confirmnewemail/(?P[^/]+)/$', 'confirm_new_email'),
+ (r'whitelist/add/?$', add_account_whitelist),
)
diff --git a/ietf/ietfauth/views.py b/ietf/ietfauth/views.py
index 7c7649e66..3d3cf2088 100644
--- a/ietf/ietfauth/views.py
+++ b/ietf/ietfauth/views.py
@@ -32,6 +32,8 @@
# Copyright The IETF Trust 2007, All Rights Reserved
+from datetime import datetime as DateTime, timedelta as TimeDelta
+
from django.conf import settings
from django.http import Http404 #, HttpResponse, HttpResponseRedirect
from django.shortcuts import render, redirect, get_object_or_404
@@ -42,10 +44,14 @@ import django.core.signing
from django.contrib.sites.models import Site
from django.contrib.auth.models import User
+import debug # pyflakes:ignore
+
from ietf.group.models import Role
-from ietf.ietfauth.forms import RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm
+from ietf.ietfauth.forms import RegistrationForm, PasswordForm, ResetPasswordForm, TestEmailForm, WhitelistForm
from ietf.ietfauth.forms import get_person_form, RoleEmailForm, NewEmailForm
from ietf.ietfauth.htpasswd import update_htpasswd_file
+from ietf.ietfauth.utils import role_required
+from ietf.mailinglists.models import Subscribed, Whitelisted
from ietf.person.models import Person, Email, Alias
from ietf.utils.mail import send_mail
@@ -85,20 +91,25 @@ def create_account(request):
if request.method == 'POST':
form = RegistrationForm(request.POST)
if form.is_valid():
- to_email = form.cleaned_data['email']
+ to_email = form.cleaned_data['email'] # This will be lowercase if form.is_valid()
+ existing = Subscribed.objects.filter(address=to_email).first()
+ ok_to_create = ( Whitelisted.objects.filter(address=to_email).exists()
+ or existing and (existing.time + TimeDelta(seconds=settings.LIST_ACCOUNT_DELAY)) < DateTime.now() )
+ if ok_to_create:
+ auth = django.core.signing.dumps(to_email, salt="create_account")
- 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
- 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,
- })
+ 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:
+ return render(request, 'registration/manual.html', { 'account_request_email': settings.ACCOUNT_REQUEST_EMAIL })
else:
form = RegistrationForm()
@@ -359,3 +370,22 @@ def test_email(request):
r.set_cookie("testmailcc", cookie)
return r
+
+@role_required('Secretariat')
+def add_account_whitelist(request):
+ success = False
+ if request.method == 'POST':
+ form = WhitelistForm(request.POST)
+ if form.is_valid():
+ address = form.cleaned_data['address']
+ entry = Whitelisted(address=address, by=request.user.person)
+ entry.save()
+ success = True
+ else:
+ form = WhitelistForm()
+
+ return render(request, 'ietfauth/whitelist_form.html', {
+ 'form': form,
+ 'success': success,
+ })
+
diff --git a/ietf/templates/ietfauth/whitelist_form.html b/ietf/templates/ietfauth/whitelist_form.html
new file mode 100644
index 000000000..9bccd27c6
--- /dev/null
+++ b/ietf/templates/ietfauth/whitelist_form.html
@@ -0,0 +1,93 @@
+{% extends "base.html" %}
+{# Copyright The IETF Trust 2016, All Rights Reserved #}
+{% load origin %}
+
+{% load bootstrap3 %}
+
+{% block title %}Set up test email address{% endblock %}
+
+{% block content %}
+ {% origin %}
+ {% if success %}
+ Whitelist entry creation successful
+
+
+
+ Please ask the requestor to try the
+ account creation form
+ again, with the whitelisted email address.
+
+
+
+ {% else %}
+ Add a whitelist entry for account creation.
+
+
+ When an email request comes in for assistance with account creation
+ because the automated account creation has failed, you can add the
+ address to an account creation whitelist here.
+
+
+ Before you do so, please complete the following 3 verification steps:
+
+ -
+
+ Has the person provided relevant information in his request, or has he simply
+ copied the text from the account creation failure message? All genuine (non-spam)
+ account creation requests seen between 2009 and 2016 for tools.ietf.org has
+ contained a reasonable request message, rather than just copy-pasting the account
+ creation failure message. If there's no proper request message, step 2 below can
+ be performed to make sure the request is bogus, but if that also fails, no further
+ effort should be needed.
+
+
+ -
+
+ Google for the person's name within the ietf site: "Jane Doe site:ietf.org". If
+ found, and the email address matches an address used in drafts or discussions,
+ things are fine, and it's OK to add the address to the whitelist using this form,
+ and ask the person to please try the account creation form again.
+
+
+ -
+
+
+
+ If google finds no trace of the person being an ietf participant, he or she could
+ still be somebody who is just getting involved in IETF work. A datatracker account
+ is probably not necessary, but in case this is a legitimate request, please email
+ the person and ask:
+
+
+
+ "Which wgs do you require a password for?"
+
+
+
+
+ This is a bit of a trick question, because it is very unlikely that somebody who
+ isn't involved in IETF work will give a reasonable response, while almost any answer
+ from somebody who is doing IETF work will show that they have some clue.
+
+
+
+
+ If the answer to this question shows clue, then add the address to the whitelist
+ using this form, and ask the person to please try the account creation form again.
+
+
+
+
+
+
+ {% endif %}
+{% endblock %}
diff --git a/ietf/templates/registration/manual.html b/ietf/templates/registration/manual.html
new file mode 100644
index 000000000..3161bd3cc
--- /dev/null
+++ b/ietf/templates/registration/manual.html
@@ -0,0 +1,21 @@
+{% extends "base.html" %}
+{# Copyright The IETF Trust 2015, All Rights Reserved #}
+{% load origin %}
+
+{% load bootstrap3 %}
+
+{% block title %}Complete account creation{% endblock %}
+
+{% block content %}
+ {% origin %}
+
+ Account creation failed
+
+
+ Manual intervention is needed to enable account creation for you.
+ Please send an email to {{ account_request_email }}
+ and explain 1) the situation and 2) your need for an account,
+ in order to receive further assistance.
+
+
+{% endblock %}