Bug 584490: restricting user email domains with blacklist.

This commit is contained in:
chenba 2010-09-07 01:50:52 -07:00
Родитель 80ada91627
Коммит 6bf2218bec
8 изменённых файлов: 253 добавлений и 27 удалений

Просмотреть файл

@ -1,11 +1,10 @@
from django.contrib import admin, messages
from django.db.utils import IntegrityError
from django.utils.encoding import smart_unicode
import jingo
from access.admin import GroupUserInline
from .models import UserProfile, BlacklistedUsername
from .models import UserProfile, BlacklistedUsername, BlacklistedEmailDomain
from .users import forms
@ -36,44 +35,56 @@ class UserAdmin(admin.ModelAdmin):
)
class BlacklistedUsernameAdmin(admin.ModelAdmin):
list_display = search_fields = ('username',)
class BlacklistModelAdmin(admin.ModelAdmin):
def add_view(self, request, form_url='', extra_context=None):
"""Override the default admin add view for bulk add."""
form = forms.BlacklistedUsernameAddForm()
form = self.model_add_form()
if request.method == 'POST':
form = forms.BlacklistedUsernameAddForm(request.POST)
form = self.model_add_form(request.POST)
if form.is_valid():
inserted = 0
duplicates = 0
for n in form.cleaned_data['usernames'].splitlines():
for x in form.cleaned_data[self.add_form_field].splitlines():
# check with teh cache
if BlacklistedUsername.blocked(n):
if self.blacklist_model.blocked(x):
duplicates += 1
continue
n = smart_unicode(n).lower().encode('utf-8')
try:
BlacklistedUsername.objects.create(username=n)
self.blacklist_model.objects.create(
**{self.model_field: x.lower()})
inserted += 1
except IntegrityError:
# although unlikely, someone else could have added
# the username.
# the same value.
# note: unless we manage the transactions manually,
# we do lose a primary id here
# we do lose a primary id here.
duplicates += 1
msg = '%s new usernames added to the blacklist.' % (inserted)
msg = '%s new values added to the blacklist.' % (inserted)
if duplicates:
msg += ' %s duplicates were ignored.' % (duplicates)
messages.success(request, msg)
form = forms.BlacklistedUsernameAddForm()
# Default django admin change list view does not print messages
# no redirect for now
# return http.HttpResponseRedirect(reverse(
# 'admin:users_blacklistedusername_changelist'))
return jingo.render(request, 'admin/blacklisted_username/add.html',
{'form': form})
form = self.model_add_form()
return jingo.render(request, self.template_path, {'form': form})
class BlacklistedUsernameAdmin(BlacklistModelAdmin):
list_display = search_fields = ('username',)
blacklist_model = BlacklistedUsername
model_field = 'username'
model_add_form = forms.BlacklistedUsernameAddForm
add_form_field = 'usernames'
template_path = 'admin/blacklisted_username/add.html'
class BlacklistedEmailDomainAdmin(BlacklistModelAdmin):
list_display = search_fields = ('domain',)
blacklist_model = BlacklistedEmailDomain
model_field = 'domain'
model_add_form = forms.BlacklistedEmailDomainAddForm
add_form_field = 'domains'
template_path = 'admin/blacklisted_email_domain/add.html'
admin.site.register(UserProfile, UserAdmin)
admin.site.register(BlacklistedUsername, BlacklistedUsernameAdmin)
admin.site.register(BlacklistedEmailDomain, BlacklistedEmailDomainAdmin)

Просмотреть файл

@ -164,5 +164,14 @@
"modified": "2010-07-21 23:32:05",
"created": "2010-07-21 23:32:05"
}
},
{
"pk": 1,
"model": "users.blacklistedemaildomain",
"fields": {
"domain": "mailinator.com",
"modified": "2010-09-07 00:31:40",
"created": "2010-09-07 00:31:40"
}
}
]

Просмотреть файл

@ -10,7 +10,7 @@ import commonware.log
import happyforms
from tower import ugettext as _, ugettext_lazy as _lazy
from .models import UserProfile, BlacklistedUsername
from .models import UserProfile, BlacklistedUsername, BlacklistedEmailDomain
import tasks
log = commonware.log.getLogger('z.users')
@ -88,6 +88,14 @@ class UserRegisterForm(happyforms.ModelForm):
if not settings.RECAPTCHA_PRIVATE_KEY:
del self.fields['recaptcha']
def clean_email(self):
d = self.cleaned_data['email'].split('@')[-1]
if BlacklistedEmailDomain.blocked(d):
raise forms.ValidationError(_('Please use an email address from a '
'different provider to complete '
'your registration.'))
return self.cleaned_data['email']
def clean_username(self):
name = self.cleaned_data['username']
if BlacklistedUsername.blocked(name):
@ -216,3 +224,23 @@ class BlacklistedUsernameAddForm(forms.Form):
self._errors['usernames'] = ErrorList([msg])
return data
class BlacklistedEmailDomainAddForm(forms.Form):
"""Form for adding blacklisted user e-mail domains in bulk fashion."""
domains = forms.CharField(
widget=forms.Textarea(attrs={'cols': 40, 'rows': 16}))
def clean(self):
super(BlacklistedEmailDomainAddForm, self).clean()
data = self.cleaned_data
if 'domains' in data:
l = filter(None, [s.strip() for s in data['domains'].splitlines()])
data['domains'] = os.linesep.join(l)
if not data.get('domains', ''):
msg = 'Please enter at least one e-mail domain to blacklist.'
self._errors['domains'] = ErrorList([msg])
return data

Просмотреть файл

@ -11,7 +11,7 @@ from django.contrib.auth.models import User as DjangoUser
from django.core.mail import send_mail
from django.db import models
from django.template import Context, loader
from django.utils.encoding import smart_unicode, smart_str
from django.utils.encoding import smart_str
import caching.base as caching
import commonware.log
@ -301,11 +301,33 @@ class BlacklistedUsername(amo.models.ModelBase):
@classmethod
def blocked(cls, username):
"""Check to see if a username is in the (cached) blacklist."""
username = smart_unicode(username).lower()
qs = cls.objects.all()
f = lambda: [u.lower() for u in qs.values_list('username', flat=True)]
blacklist = caching.cached_with(qs, f, 'blocked')
return username in blacklist
return username.lower() in blacklist
class BlacklistedEmailDomain(amo.models.ModelBase):
"""Blacklisted user e-mail domains."""
domain = models.CharField(max_length=255, unique=True, default='',
blank=False)
def __unicode__(self):
return self.domain
@classmethod
def blocked(cls, domain):
qs = cls.objects.all()
f = lambda: list(qs.values_list('domain', flat=True))
blacklist = caching.cached_with(qs, f, 'blocked')
# because there isn't a good way to know if the domain is
# "example.com" or "example.co.jp", we'll re-construct it...
# so if it's "bad.example.co.jp", the following check the
# values in ['bad.example.co.jp', 'example.co.jp', 'co.jp']
x = domain.lower().split('.')
for d in ['.'.join(x[y:]) for y in range(len(x) - 1)]:
if d in blacklist:
return True
class PersonaAuthor(unicode):

Просмотреть файл

@ -0,0 +1,43 @@
{% extends "admin/base.html" %}
{% from 'includes/forms.html' import required %}
{% block extrahead %}
{{ super() }}
<link rel="stylesheet" href="{{ MEDIA_URL }}css/zamboni/admin-django.css">
{% endblock %}
{% block title %}{{ page_title('Add Blacklisted E-mail Domains') }}{% endblock %}
{% block bodyclass %}users-blacklistedemaildomain change-form{% endblock %}
{% block content %}
<div class="breadcrumbs">
<a href="{{ url('admin:index') }}">Home</a> &rsaquo;
<a href="{{ url('admin:app_list', app_label='users') }}">Users</a> &rsaquo;
<a href="{{ url('admin:users_blacklistedemaildomain_changelist') }}">Blacklisted e-mail domains</a> &rsaquo;
Add
</div>
<div id="content" class="colM">
<h2>Add Blacklisted E-mail Domains</h2>
<div id="content-main">
{% include "messages.html" %}
{% if form %}
<form method="post" action="">
{{ csrf() }}
<p>Enter one domain per line.</p>
<div class="form-row">
{{ form.domains.errors|safe }}
<label for="id_usernames">Blacklisted E-mail Domains {{ required() }}</label>
{{ form.domains|safe }}
</div>
<div class="submit-row">
<input type="submit" name="_save" class="default" value="Save">
</div>
</form>
{% endif %}
</div>
</div>
{% endblock content %}

Просмотреть файл

@ -301,6 +301,16 @@ class TestUserRegisterForm(UserFormBase):
self.assertFormError(r, 'form', 'username',
'This username is invalid.')
def test_invalid_email_domain(self):
data = {'email': 'fake@mailinator.com',
'password': 'xxx',
'password2': 'xxx',
'username': 'trulyfake', }
r = self.client.post('/en-US/firefox/users/register', data)
self.assertFormError(r, 'form', 'email',
'Please use an email address from a different '
'provider to complete your registration.')
def test_invalid_homepage(self):
data = {'homepage': 'example.com:alert(String.fromCharCode(88,83,83)'}
m = 'This URL has an invalid format. '
@ -352,7 +362,28 @@ class TestBlacklistedUsernameAdminAddForm(UserFormBase):
url = reverse('admin:users_blacklistedusername_add')
data = {'usernames': "IE6Fan\nfubar\n\n", }
r = self.client.post(url, data)
msg = '1 new usernames added to the blacklist. '
msg = '1 new values added to the blacklist. '
msg += '1 duplicates were ignored.'
self.assertContains(r, msg)
self.assertNotContains(r, 'fubar')
class TestBlacklistedEmailDomainAdminAddForm(UserFormBase):
def test_no_domains(self):
self.client.login(username='testo@example.com', password='foo')
url = reverse('admin:users_blacklistedemaildomain_add')
data = {'domains': "\n\n", }
r = self.client.post(url, data)
msg = 'Please enter at least one e-mail domain to blacklist.'
self.assertFormError(r, 'form', 'domains', msg)
def test_add(self):
self.client.login(username='testo@example.com', password='foo')
url = reverse('admin:users_blacklistedemaildomain_add')
data = {'domains': "mailinator.com\ntrash-mail.de\n\n", }
r = self.client.post(url, data)
msg = '1 new values added to the blacklist. '
msg += '1 duplicates were ignored.'
self.assertContains(r, msg)
self.assertNotContains(r, 'fubar')

Просмотреть файл

@ -12,7 +12,8 @@ import amo.test_utils
from addons.models import Addon, AddonUser
from bandwagon.models import Collection
from reviews.models import Review
from users.models import UserProfile, get_hexdigest, BlacklistedUsername
from users.models import UserProfile, get_hexdigest, BlacklistedUsername,\
BlacklistedEmailDomain
class TestUserProfile(amo.test_utils.ExtraSetup, test_utils.TestCase):
@ -165,3 +166,12 @@ class TestBlacklistedUsername(amo.test_utils.ExtraSetup, test_utils.TestCase):
def test_blocked(self):
eq_(BlacklistedUsername.blocked('IE6Fan'), True)
eq_(BlacklistedUsername.blocked('testo'), False)
class TestBlacklistedEmailDomain(amo.test_utils.ExtraSetup,
test_utils.TestCase):
fixtures = ['users/test_backends']
def test_blocked(self):
eq_(BlacklistedEmailDomain.blocked('mailinator.com'), True)
assert not BlacklistedEmailDomain.blocked('mozilla.com')

Просмотреть файл

@ -0,0 +1,72 @@
CREATE TABLE `users_blacklistedemaildomain` (
`id` int(11) NOT NULL auto_increment,
`domain` varchar(255) NOT NULL default '',
`created` datetime NOT NULL,
`modified` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `domain` (`domain`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO
`users_blacklistedemaildomain` (`domain`, `created`, `modified`)
VALUES
('mailinator.com',NOW(),NOW()),
('mailinator2.com',NOW(),NOW()),
('fuckingduh.com',NOW(),NOW()),
('sogetthis.com',NOW(),NOW()),
('mailin8r.com',NOW(),NOW()),
('mailinator.net',NOW(),NOW()),
('klassmaster.com',NOW(),NOW()),
('putthisinyourspamdatabase.com',NOW(),NOW()),
('thisisnotmyrealemail.com',NOW(),NOW()),
('binkmail.com',NOW(),NOW()),
('spamhereplease.com',NOW(),NOW()),
('spamherelots.com',NOW(),NOW()),
('sendspamhere.com',NOW(),NOW()),
('chogmail.com',NOW(),NOW()),
('spamthisplease.com',NOW(),NOW()),
('frapmail.com',NOW(),NOW()),
('obobbo.com',NOW(),NOW()),
('devnullmail.com',NOW(),NOW()),
('bumpymail.com',NOW(),NOW()),
('centermail.com',NOW(),NOW()),
('centermail.net',NOW(),NOW()),
('discardmail.com',NOW(),NOW()),
('dodgeit.com',NOW(),NOW()),
('e4ward.com',NOW(),NOW()),
('emailias.com',NOW(),NOW()),
('fakeinformation.com',NOW(),NOW()),
('front14.org',NOW(),NOW()),
('ghosttexter.de',NOW(),NOW()),
('jetable.net',NOW(),NOW()),
('kasmail.com',NOW(),NOW()),
('link2mail.net',NOW(),NOW()),
('mailexpire.com',NOW(),NOW()),
('mailmoat.com',NOW(),NOW()),
('messagebeamer.de',NOW(),NOW()),
('mytrashmail.com',NOW(),NOW()),
('nervmich.net',NOW(),NOW()),
('netmails.net',NOW(),NOW()),
('netzidiot.de',NOW(),NOW()),
('nurfuerspam.de',NOW(),NOW()),
('oneoffemail.com',NOW(),NOW()),
('pookmail.com',NOW(),NOW()),
('privacy.net',NOW(),NOW()),
('punkass.com',NOW(),NOW()),
('sneakemail.com',NOW(),NOW()),
('sofort-mail.de',NOW(),NOW()),
('spam.la',NOW(),NOW()),
('spambob.com',NOW(),NOW()),
('spambob.net',NOW(),NOW()),
('spambob.org',NOW(),NOW()),
('spamex.com',NOW(),NOW()),
('spamgourmet.com',NOW(),NOW()),
('spamhole.com',NOW(),NOW()),
('spaminator.de',NOW(),NOW()),
('spammotel.com',NOW(),NOW()),
('spamtrail.com',NOW(),NOW()),
('trash-mail.de',NOW(),NOW()),
('trashmail.net',NOW(),NOW()),
('wuzup.net',NOW(),NOW()),
('suremail.info',NOW(),NOW()),
('spammote.com',NOW(),NOW());