Skip to content

Commit 4db4dd1

Browse files
committed
Merged in ^/personal/henrik/6.79.1-gdpr@15180 containing GDPR-adaptations.
- Legacy-Id: 15181
2 parents 5c5c044 + 46bee81 commit 4db4dd1

46 files changed

Lines changed: 534 additions & 136 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

changelog

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,67 @@
1+
ietfdb (6.81.0) ietf; urgency=medium
2+
3+
This release contains code and schema changes necessary for compliance
4+
with the European GDPR (General Data Protection Regulation). It provides
5+
necessary GUI and internal changes, but lacks some admin utilities, and
6+
also data migration routines which takes some time to run, and therefore
7+
will be packaged in a separate release and applied separately.
8+
9+
From the commit log:
10+
11+
* Updated the personal information page with reviewed text from legal
12+
counsel. Fixes issue #2503.
13+
14+
* Removed the Person.address field, which is not being used. This was a
15+
legacy from the 2001 perl-based datatracker tables. Fixes issue #2504.
16+
17+
* Added django-simple-history and replaced the old (and unused)
18+
PersonHistory class with a history=HistoricalRecords() field on Person.
19+
Added the needed migrations and changes to admin, resources, and settings.
20+
Related to issues #2505 and #2507.
21+
22+
* Added a new field name_from_draft to Person, to hold the name field
23+
equivalent as captured from drafts, in case name has been modified by the
24+
user and we're asked to remove that info under GDPR. Added history for
25+
Email, and also an origin field to capture from where we got an email
26+
address (draft name, username, meeting registration, etc.) Added a
27+
log.assertion() to Email.save() in order to ensure we don't create any
28+
email without setting origin.
29+
30+
* Added origin information to all places where we create email address
31+
entries.
32+
33+
* Removed all references to the removed Person.affiliation field.
34+
35+
* Added email origin information to some function calls that needed it.
36+
37+
* Overwrite earlier email origin when we've picked up the address from a
38+
submission.
39+
40+
* Added a consent field to the Person model.
41+
42+
* Disallow profile changes without consent given. Together with previous
43+
commits this fixes issues #2505 and #2507.
44+
45+
* Added another category of personal information to the
46+
personal-information page, after review of personal information in the
47+
code. Completes issue #2501.
48+
49+
* Added a dagger at the end of some fields in the account data forms to
50+
signify consent-based fields, and made the consent field required.
51+
52+
* Simplified the email.origin assignment code for outgoing liaisons.
53+
54+
* Fixed a long-standing bug in the liaison.name() code.
55+
56+
* Added assingment of the person.name_from_draft field on draft
57+
submission.
58+
59+
60+
* Updated the edit_profile template with information about consent-based
61+
fields. Fixes issue #2502.
62+
63+
-- Henrik Levkowetz <henrik@levkowetz.com> 28 May 2018 11:15:34 +0000
64+
165
ietfdb (6.80.1) ietf; urgency=medium
266

367
This is a bugfix release which also clears the slate for the upcoming

ietf/doc/tests_draft.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1001,7 +1001,7 @@ def test_doc_change_shepherd_email(self):
10011001
doc.shepherd = Email.objects.get(person__user__username="plain")
10021002
doc.save_with_history([DocEvent.objects.create(doc=doc, rev=doc.rev, type="changed_shepherd", by=Person.objects.get(user__username="secretary"), desc="Test")])
10031003

1004-
new_email = Email.objects.create(address="anotheremail@example.com", person=doc.shepherd.person)
1004+
new_email = Email.objects.create(address="anotheremail@example.com", person=doc.shepherd.person, origin=doc.shepherd.person.user.username)
10051005

10061006
r = self.client.get(url)
10071007
self.assertEqual(r.status_code, 200)
@@ -1435,8 +1435,8 @@ def setUp(self):
14351435
expires=datetime.datetime.now() + datetime.timedelta(days=settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
14361436
group=mars_wg,
14371437
)
1438-
p = Person.objects.create(address="basea_author")
1439-
e = Email.objects.create(address="basea_author@example.com", person=p)
1438+
p = PersonFactory(name=u"basea_author")
1439+
e = Email.objects.create(address="basea_author@example.com", person=p, origin=p.user.username)
14401440
self.basea.documentauthor_set.create(person=p, email=e, order=1)
14411441

14421442
self.baseb = Document.objects.create(
@@ -1448,8 +1448,8 @@ def setUp(self):
14481448
expires=datetime.datetime.now() - datetime.timedelta(days = 365 - settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
14491449
group=mars_wg,
14501450
)
1451-
p = Person.objects.create(name="baseb_author")
1452-
e = Email.objects.create(address="baseb_author@example.com", person=p)
1451+
p = PersonFactory(name=u"baseb_author")
1452+
e = Email.objects.create(address="baseb_author@example.com", person=p, origin=p.user.username)
14531453
self.baseb.documentauthor_set.create(person=p, email=e, order=1)
14541454

14551455
self.replacea = Document.objects.create(
@@ -1461,8 +1461,8 @@ def setUp(self):
14611461
expires=datetime.datetime.now() + datetime.timedelta(days = settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
14621462
group=mars_wg,
14631463
)
1464-
p = Person.objects.create(name="replacea_author")
1465-
e = Email.objects.create(address="replacea_author@example.com", person=p)
1464+
p = PersonFactory(name=u"replacea_author")
1465+
e = Email.objects.create(address="replacea_author@example.com", person=p, origin=p.user.username)
14661466
self.replacea.documentauthor_set.create(person=p, email=e, order=1)
14671467

14681468
self.replaceboth = Document.objects.create(
@@ -1474,8 +1474,8 @@ def setUp(self):
14741474
expires=datetime.datetime.now() + datetime.timedelta(days = settings.INTERNET_DRAFT_DAYS_TO_EXPIRE),
14751475
group=mars_wg,
14761476
)
1477-
p = Person.objects.create(name="replaceboth_author")
1478-
e = Email.objects.create(address="replaceboth_author@example.com", person=p)
1477+
p = PersonFactory(name=u"replaceboth_author")
1478+
e = Email.objects.create(address="replaceboth_author@example.com", person=p, origin=p.user.username)
14791479
self.replaceboth.documentauthor_set.create(person=p, email=e, order=1)
14801480

14811481
self.basea.set_state(State.objects.get(used=True, type="draft", slug="active"))

ietf/doc/views_draft.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -954,6 +954,9 @@ def edit_shepherd(request, name):
954954
events = []
955955

956956
doc.shepherd = form.cleaned_data['shepherd']
957+
if doc.shepherd and not doc.shepherd.origin:
958+
doc.shepherd.origin = 'shepherd: %s' % doc.name
959+
doc.shepherd.save()
957960

958961
c = DocEvent(type="added_comment", doc=doc, rev=doc.rev, by=request.user.person)
959962
c.desc = "Document shepherd changed to "+ (doc.shepherd.person.name if doc.shepherd else "(None)")

ietf/doc/views_search.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ def retrieve_search_results(form, all_types=False):
175175
if by == "author":
176176
docs = docs.filter(
177177
Q(documentauthor__person__alias__name__icontains=query["author"]) |
178-
Q(documentauthor__person__affiliation__icontains=query["author"]) |
179178
Q(documentauthor__person__email__address__icontains=query["author"])
180179
)
181180
elif by == "group":

ietf/group/views.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -958,6 +958,10 @@ def diff(attr, name):
958958
group.role_set.filter(name=slug).delete()
959959
for e in new:
960960
Role.objects.get_or_create(name_id=slug, email=e, group=group, person=e.person)
961+
if not e.origin or e.origin == e.person.user.username:
962+
e.origin = "role: %s %s" % (group.acronym, slug)
963+
e.save()
964+
961965
added = set(new) - set(old)
962966
deleted = set(old) - set(new)
963967
if added:
@@ -1206,6 +1210,9 @@ def stream_edit(request, acronym):
12061210
group.role_set.filter(name=slug).delete()
12071211
for e in new:
12081212
Role.objects.get_or_create(name_id=slug, email=e, group=group, person=e.person)
1213+
if not e.origin or e.origin == e.person.user.username:
1214+
e.origin = "role: %s %s" % (group.acronym, slug)
1215+
e.save()
12091216

12101217
return redirect("ietf.group.views.streams")
12111218
else:

ietf/help/tests_views.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from ietf.utils.test_utils import TestCase
88
from ietf.doc.models import StateType
99

10-
class StateHelpTest(TestCase):
10+
class HelpPageTests(TestCase):
1111

1212
def test_state_index(self):
1313
url = reverse('ietf.help.views.state_index')
@@ -21,3 +21,7 @@ def test_state_index(self):
2121
self.assertIn(name, content)
2222

2323

24+
def test_personal_information_help(self):
25+
r = self.client.get('/help/personal-information')
26+
self.assertContains(r, 'personal information')
27+
self.assertContains(r, 'GDPR')

ietf/help/urls.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
# Copyright The IETF Trust 2013-2018, All Rights Reserved
2+
3+
from django.views.generic import TemplateView
14

25
from ietf.help import views
36
from ietf.utils.urls import url
@@ -6,5 +9,6 @@
69
url(r'^state/(?P<doc>[-\w]+)/(?P<type>[-\w]+)/?$', views.state),
710
url(r'^state/(?P<doc>[-\w]+)/?$', views.state),
811
url(r'^state/?$', views.state_index),
12+
url(r'^personal-information/?$', TemplateView.as_view(template_name='help/personal-information.html'), name='personal-information'),
913
]
1014

ietf/ietfauth/forms.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
# Copyright The IETF Trust 2016, All Rights Reserved
2+
# -*- coding: utf-8 -*-
3+
from __future__ import unicode_literals, print_function
4+
15
import re
26
from unidecode import unidecode
37

@@ -94,6 +98,9 @@ class PersonForm(forms.ModelForm):
9498
class Meta:
9599
model = Person
96100
exclude = exclude_list
101+
widgets = {
102+
'consent': forms.widgets.CheckboxInput,
103+
}
97104

98105
def __init__(self, *args, **kwargs):
99106
super(PersonForm, self).__init__(*args, **kwargs)
@@ -105,6 +112,12 @@ def __init__(self, *args, **kwargs):
105112
if self.initial.get("ascii") == self.initial.get("name"):
106113
self.initial["ascii"] = ""
107114

115+
for f in ['name', 'ascii', 'ascii_short', 'biography', 'photo', 'photo_thumb', ]:
116+
if f in self.fields:
117+
self.fields[f].label += ' \u2020'
118+
119+
self.fields["consent"].required = True
120+
108121
self.unidecoded_ascii = False
109122

110123
if self.data and not self.data.get("ascii", "").strip():
@@ -135,6 +148,11 @@ def clean_ascii_short(self):
135148
prevent_system_name(name)
136149
return ascii_cleaner(name)
137150

151+
def clean_consent(self):
152+
consent = self.cleaned_data.get('consent')
153+
if consent == False:
154+
raise forms.ValidationError("In order to modify your profile data, you must permit the IETF to use the uploaded data.")
155+
138156
return PersonForm(*args, **kwargs)
139157

140158

ietf/ietfauth/tests.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -207,9 +207,9 @@ def test_profile(self):
207207
"name": u"Test Nãme",
208208
"ascii": u"Test Name",
209209
"ascii_short": u"T. Name",
210-
"address": "Test address",
211210
"affiliation": "Test Org",
212211
"active_emails": email_address,
212+
"consent": True,
213213
}
214214

215215
# edit details - faulty ASCII
@@ -309,7 +309,7 @@ def test_reset_password(self):
309309
user.set_password("forgotten")
310310
user.save()
311311
p = Person.objects.create(name="Some One", ascii="Some One", user=user)
312-
Email.objects.create(address=user.username, person=p)
312+
Email.objects.create(address=user.username, person=p, origin=user.username)
313313

314314
# get
315315
r = self.client.get(url)
@@ -419,7 +419,7 @@ def test_change_password(self):
419419
user.set_password("password")
420420
user.save()
421421
p = Person.objects.create(name="Some One", ascii="Some One", user=user)
422-
Email.objects.create(address=user.username, person=p)
422+
Email.objects.create(address=user.username, person=p, origin=user.username)
423423

424424
# log in
425425
r = self.client.post(redir_url, {"username":user.username, "password":"password"})
@@ -466,8 +466,8 @@ def test_change_username(self):
466466
user.set_password("password")
467467
user.save()
468468
p = Person.objects.create(name="Some One", ascii="Some One", user=user)
469-
Email.objects.create(address=user.username, person=p)
470-
Email.objects.create(address="othername@example.org", person=p)
469+
Email.objects.create(address=user.username, person=p, origin=user.username)
470+
Email.objects.create(address="othername@example.org", person=p, origin=user.username)
471471

472472
# log in
473473
r = self.client.post(redir_url, {"username":user.username, "password":"password"})

ietf/ietfauth/views.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,7 @@ def confirm_account(request, auth):
172172
Alias.objects.create(person=person, name=name)
173173

174174
if not email_obj:
175-
email_obj = Email.objects.create(address=email, person=person)
175+
email_obj = Email.objects.create(address=email, person=person, origin=user.username)
176176
else:
177177
if not email_obj.person:
178178
email_obj.person = person
@@ -251,6 +251,8 @@ def profile(request):
251251
email.primary = email.address == primary_email
252252
if email.primary and not email.active:
253253
email.active = True
254+
if not email.origin:
255+
email.origin = person.user.username
254256
email.save()
255257

256258
# Make sure the alias table contains any new and/or old names.
@@ -293,7 +295,7 @@ def confirm_new_email(request, auth):
293295
can_confirm = form.is_valid() and email
294296
new_email_obj = None
295297
if request.method == 'POST' and can_confirm and request.POST.get("action") == "confirm":
296-
new_email_obj = Email.objects.create(address=email, person=person)
298+
new_email_obj = Email.objects.create(address=email, person=person, origin=username)
297299

298300
return render(request, 'registration/confirm_new_email.html', {
299301
'username': username,

0 commit comments

Comments
 (0)