Skip to content

Commit

Permalink
Add an instance config option for stricter anonymity
Browse files Browse the repository at this point in the history
There is a view ('all-messages-from-the-same-author-as') which, given a
message slug, allows you to list any other messages with the same author
email address as the message with that slug. In situations where anonymity
is important, this is undesirable - someone might send a sensitive message
with a false name and an uncontroversial one with their real name, in
which case this view would reveal that they're sent by the same person.

This commit introduces a new instance config option
('email_and_name_must_match') which means that the
'all-messages-from-the-same-author-as' view will only show messages that
have the same author_email *and* author_name, to avoid this information
leak.

Fixes mysociety/alpaca#35
Fixes #1194
  • Loading branch information
mhl committed Nov 30, 2016
1 parent 517713c commit 92a583b
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 3 deletions.
20 changes: 20 additions & 0 deletions instance/migrations/0003_add_name_and_email_match_config_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals

from django.db import models, migrations


class Migration(migrations.Migration):

dependencies = [
('instance', '0002_writeitinstanceconfig_allow_anonymous_messages'),
]

operations = [
migrations.AddField(
model_name='writeitinstanceconfig',
name='email_and_name_must_match',
field=models.BooleanField(default=False, help_text='When showing other messages by the same author, the public name must match as well as the hidden email'),
preserve_default=True,
),
]
5 changes: 5 additions & 0 deletions instance/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,11 @@ class WriteItInstanceConfig(models.Model):
allow_anonymous_messages = models.BooleanField(
help_text=_("Messages can have empty Author \
names"), default=False)
email_and_name_must_match = models.BooleanField(
help_text=_(
"When showing other messages by the same author, the "
"public name must match as well as the hidden email"),
default=False)

custom_from_domain = models.CharField(max_length=512, null=True, blank=True)
email_host = models.CharField(max_length=512, null=True, blank=True)
Expand Down
71 changes: 71 additions & 0 deletions nuntium/tests/see_all_messages_from_a_person_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,3 +168,74 @@ def test_unmoderated_messages_not_included(self):
self.assertIn('m: yes, p: yes, c: yes', response.content)
self.assertIn('m: null, p: yes, c: yes', response.content)
self.assertNotIn('m: no, p: yes, c: yes', response.content)

def test_stricter_anonymisation_no_link_just_on_email(self):
recipient = Person.objects.first()
Message.objects.create(
writeitinstance=self.writeitinstance,
public=True,
confirmated=True,
moderated=True,
subject='joe as name, joe as email',
content='Random content',
author_name='Joe',
author_email='joe@example.com',
persons=[recipient],
)
Message.objects.create(
writeitinstance=self.writeitinstance,
public=True,
confirmated=True,
moderated=True,
subject='anon as name, joe as email',
content='More random content',
author_name='Anon',
author_email='joe@example.com',
persons=[recipient],
)
Message.objects.create(
writeitinstance=self.writeitinstance,
public=True,
confirmated=True,
moderated=True,
subject='joe as name, joseph as email',
content='A bit more content',
author_name='Joe',
author_email='joseph@example.com',
persons=[recipient],
)
config = self.writeitinstance.config
config.email_and_name_must_match = True
config.save()

def get_url(message_slug):
return reverse(
'all-messages-from-the-same-author-as',
subdomain=self.writeitinstance.slug,
kwargs={
'message_slug': message_slug
})

url = get_url('joe-as-name-joseph-as-email')
response = self.client.get(url)
messages_for_page = response.context['object_list']
self.assertEquals(1, len(messages_for_page))
self.assertEquals(
messages_for_page[0].subject,
'joe as name, joseph as email')

url = get_url('anon-as-name-joe-as-email')
response = self.client.get(url)
messages_for_page = response.context['object_list']
self.assertEquals(1, len(messages_for_page))
self.assertEquals(
messages_for_page[0].subject,
'anon as name, joe as email')

url = get_url('joe-as-name-joe-as-email')
response = self.client.get(url)
messages_for_page = response.context['object_list']
self.assertEquals(1, len(messages_for_page))
self.assertEquals(
messages_for_page[0].subject,
'joe as name, joe as email')
13 changes: 10 additions & 3 deletions nuntium/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,10 +275,17 @@ def get_queryset(self):
writeitinstance=self.writeitinstance,
author_email=self.message.author_email
)
if self.message.author_name == '':
qs = qs.filter(author_name='')
# There's a config option for stricter anonymity, which means
# that both author_name and author_email must match:
if self.writeitinstance.config.email_and_name_must_match:
qs = qs.filter(author_name=self.message.author_name)
else:
qs = qs.exclude(author_name='')
# By default, don't mix up anonymous (author_name is blank)
# messages with non-anonymous (author_name is present) messages.
if self.message.author_name == '':
qs = qs.filter(author_name='')
else:
qs = qs.exclude(author_name='')
return qs

def get_context_data(self, **kwargs):
Expand Down

0 comments on commit 92a583b

Please sign in to comment.