Skip to content

Commit fffe0c1

Browse files
feat: disallow '/' from Person.name (ietf-tools#4594)
* feat: disallow '/' from Person.name * chore: migrate Person/HistoricalPerson to include new name validator * test: test name validation
1 parent 9194632 commit fffe0c1

3 files changed

Lines changed: 41 additions & 5 deletions

File tree

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Generated by Django 2.2.28 on 2022-10-17 10:18
2+
3+
from django.db import migrations, models
4+
import ietf.person.models
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('person', '0025_chat_and_polls_apikey'),
11+
]
12+
13+
operations = [
14+
migrations.AlterField(
15+
model_name='historicalperson',
16+
name='name',
17+
field=models.CharField(db_index=True, help_text='Preferred long form of name.', max_length=255, validators=[ietf.person.models.name_character_validator], verbose_name='Full Name (Unicode)'),
18+
),
19+
migrations.AlterField(
20+
model_name='person',
21+
name='name',
22+
field=models.CharField(db_index=True, help_text='Preferred long form of name.', max_length=255, validators=[ietf.person.models.name_character_validator], verbose_name='Full Name (Unicode)'),
23+
),
24+
]

ietf/person/models.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
from django.conf import settings
1515
from django.contrib.auth.models import User
16-
from django.core.exceptions import ObjectDoesNotExist
16+
from django.core.exceptions import ObjectDoesNotExist, ValidationError
1717
from django.core.validators import validate_email
1818
from django.db import models
1919
from django.template.loader import render_to_string
@@ -35,13 +35,18 @@
3535
from ietf.utils.models import ForeignKey, OneToOneField
3636

3737

38+
def name_character_validator(value):
39+
if '/' in value:
40+
raise ValidationError('Name cannot contain "/" character.')
41+
42+
3843
class Person(models.Model):
3944
history = HistoricalRecords()
4045
user = OneToOneField(User, blank=True, null=True, on_delete=models.SET_NULL)
4146
time = models.DateTimeField(default=datetime.datetime.now) # When this Person record entered the system
4247
# The normal unicode form of the name. This must be
4348
# set to the same value as the ascii-form if equal.
44-
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True, help_text="Preferred long form of name.")
49+
name = models.CharField("Full Name (Unicode)", max_length=255, db_index=True, help_text="Preferred long form of name.", validators=[name_character_validator])
4550
plain = models.CharField("Plain Name correction (Unicode)", max_length=64, default='', blank=True, help_text="Use this if you have a Spanish double surname. Don't use this for nicknames, and don't use it unless you've actually observed that the datatracker shows your name incorrectly.")
4651
# The normal ascii-form of the name.
4752
ascii = models.CharField("Full Name (ASCII)", max_length=255, help_text="Name as rendered in ASCII (Latin, unaccented) characters.")
@@ -262,7 +267,7 @@ def cdn_photo_url(self, size=80):
262267

263268

264269
class PersonExtResource(models.Model):
265-
person = ForeignKey(Person)
270+
person = ForeignKey(Person)
266271
name = models.ForeignKey(ExtResourceName, on_delete=models.CASCADE)
267272
display_name = models.CharField(max_length=255, default='', blank=True)
268273
value = models.CharField(max_length=2083) # 2083 is the maximum legal URL length
@@ -366,7 +371,7 @@ def salt():
366371
("/api/iesg/position", "/api/iesg/position", "Area Director"),
367372
("/api/v2/person/person", "/api/v2/person/person", "Robot"),
368373
("/api/meeting/session/video/url", "/api/meeting/session/video/url", "Recording Manager"),
369-
("/api/notify/meeting/registration", "/api/notify/meeting/registration", "Robot"),
374+
("/api/notify/meeting/registration", "/api/notify/meeting/registration", "Robot"),
370375
("/api/notify/meeting/bluesheet", "/api/notify/meeting/bluesheet", "Recording Manager"),
371376
("/api/notify/session/attendees", "/api/notify/session/attendees", "Recording Manager"),
372377
("/api/notify/session/chatlog", "/api/notify/session/chatlog", "Recording Manager"),
@@ -444,4 +449,4 @@ class Meta:
444449

445450
class PersonApiKeyEvent(PersonEvent):
446451
key = ForeignKey(PersonalApiKey)
447-
452+

ietf/person/tests.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from pyquery import PyQuery
1111

1212

13+
from django.core.exceptions import ValidationError
1314
from django.http import HttpRequest
1415
from django.test import override_settings
1516
from django.urls import reverse as urlreverse
@@ -191,6 +192,12 @@ def test_cdn_photo_url_cdn_off(self):
191192
p = PersonFactory(with_bio=True)
192193
self.assertNotIn('cdn-cgi/photo',p.cdn_photo_url())
193194

195+
def test_invalid_name_characters_rejected(self):
196+
slash_person = PersonFactory.build(name='I have a /', user=None) # build() does not save the new object
197+
with self.assertRaises(ValidationError):
198+
slash_person.full_clean() # calls validators (save() does *not*)
199+
200+
194201
class PersonUtilsTests(TestCase):
195202
def test_determine_merge_order(self):
196203
p1 = get_person_no_user()

0 commit comments

Comments
 (0)