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: +

    +
  1. + + 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. + +
  2. +
  3. + + 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. + +
  4. +
  5. + +

    + + 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. + +

    +
  6. +
+

+
+ {% csrf_token %} + {% bootstrap_form form %} + + {% buttons %} + + {% endbuttons %} +
+ {% 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 %}