Skip to content

Commit 640855f

Browse files
committed
Merged in [19744] from jennifer@painless-security.com:
Treat application/octet-stream as text/markdown for '.md' materials uploads. Refactor FileUploadForm hierarchy to reduce boilerplate. Fixes ietf-tools#3163. - Legacy-Id: 19746 Note: SVN reference [19744] has been migrated to Git commit b04254a
2 parents c0df2cd + b04254a commit 640855f

6 files changed

Lines changed: 667 additions & 526 deletions

File tree

ietf/meeting/forms.py

Lines changed: 82 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
import os
77
import datetime
88
import json
9+
import re
10+
11+
from pathlib import Path
912

1013
from django import forms
1114
from django.conf import settings
@@ -324,14 +327,19 @@ def __init__(self, *args, **kwargs):
324327
self.fields['date'].widget.attrs['disabled'] = True
325328

326329
class FileUploadForm(forms.Form):
330+
"""Base class for FileUploadForms
331+
332+
Abstract base class - subclasses must fill in the doc_type value with
333+
the type of document they handle.
334+
"""
327335
file = forms.FileField(label='File to upload')
328336

337+
doc_type = '' # subclasses must set this
338+
329339
def __init__(self, *args, **kwargs):
330-
doc_type = kwargs.pop('doc_type')
331-
assert doc_type in settings.MEETING_VALID_UPLOAD_EXTENSIONS
332-
self.doc_type = doc_type
333-
self.extensions = settings.MEETING_VALID_UPLOAD_EXTENSIONS[doc_type]
334-
self.mime_types = settings.MEETING_VALID_UPLOAD_MIME_TYPES[doc_type]
340+
assert self.doc_type in settings.MEETING_VALID_UPLOAD_EXTENSIONS
341+
self.extensions = settings.MEETING_VALID_UPLOAD_EXTENSIONS[self.doc_type]
342+
self.mime_types = settings.MEETING_VALID_UPLOAD_MIME_TYPES[self.doc_type]
335343
super(FileUploadForm, self).__init__(*args, **kwargs)
336344
label = '%s file to upload. ' % (self.doc_type.capitalize(), )
337345
if self.doc_type == "slides":
@@ -344,22 +352,88 @@ def clean_file(self):
344352
file = self.cleaned_data['file']
345353
validate_file_size(file)
346354
ext = validate_file_extension(file, self.extensions)
355+
356+
# override the Content-Type if needed
357+
if file.content_type in 'application/octet-stream':
358+
content_type_map = settings.MEETING_APPLICATION_OCTET_STREAM_OVERRIDES
359+
filename = Path(file.name)
360+
if filename.suffix in content_type_map:
361+
file.content_type = content_type_map[filename.suffix]
362+
self.cleaned_data['file'] = file
363+
347364
mime_type, encoding = validate_mime_type(file, self.mime_types)
348365
if not hasattr(self, 'file_encoding'):
349366
self.file_encoding = {}
350367
self.file_encoding[file.name] = encoding or None
351368
if self.mime_types:
352369
if not file.content_type in settings.MEETING_VALID_UPLOAD_MIME_FOR_OBSERVED_MIME[mime_type]:
353370
raise ValidationError('Upload Content-Type (%s) is different from the observed mime-type (%s)' % (file.content_type, mime_type))
354-
if mime_type in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS:
355-
if not ext in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS[mime_type]:
371+
# We just validated that file.content_type is safe to accept despite being identified
372+
# as a different MIME type by the validator. Check extension based on file.content_type
373+
# because that better reflects the intention of the upload client.
374+
if file.content_type in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS:
375+
if not ext in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS[file.content_type]:
356376
raise ValidationError('Upload Content-Type (%s) does not match the extension (%s)' % (file.content_type, ext))
357-
if mime_type in ['text/html', ] or ext in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS['text/html']:
377+
if (file.content_type in ['text/html', ]
378+
or ext in settings.MEETING_VALID_MIME_TYPE_EXTENSIONS.get('text/html', [])):
358379
# We'll do html sanitization later, but for frames, we fail here,
359380
# as the sanitized version will most likely be useless.
360381
validate_no_html_frame(file)
361382
return file
362383

384+
385+
class UploadBlueSheetForm(FileUploadForm):
386+
doc_type = 'bluesheets'
387+
388+
389+
class ApplyToAllFileUploadForm(FileUploadForm):
390+
"""FileUploadField that adds an apply_to_all checkbox
391+
392+
Checkbox can be disabled by passing show_apply_to_all_checkbox=False to the constructor.
393+
This entirely removes the field from the form.
394+
"""
395+
# Note: subclasses must set doc_type for FileUploadForm
396+
apply_to_all = forms.BooleanField(label='Apply to all group sessions at this meeting',initial=True,required=False)
397+
398+
def __init__(self, show_apply_to_all_checkbox, *args, **kwargs):
399+
super().__init__(*args, **kwargs)
400+
if not show_apply_to_all_checkbox:
401+
self.fields.pop('apply_to_all')
402+
else:
403+
self.order_fields(
404+
sorted(
405+
self.fields.keys(),
406+
key=lambda f: 'zzzzzz' if f == 'apply_to_all' else f
407+
)
408+
)
409+
410+
class UploadMinutesForm(ApplyToAllFileUploadForm):
411+
doc_type = 'minutes'
412+
413+
414+
class UploadAgendaForm(ApplyToAllFileUploadForm):
415+
doc_type = 'agenda'
416+
417+
418+
class UploadSlidesForm(ApplyToAllFileUploadForm):
419+
doc_type = 'slides'
420+
title = forms.CharField(max_length=255)
421+
422+
def __init__(self, session, *args, **kwargs):
423+
super().__init__(*args, **kwargs)
424+
self.session = session
425+
426+
def clean_title(self):
427+
title = self.cleaned_data['title']
428+
# The current tables only handles Unicode BMP:
429+
if ord(max(title)) > 0xffff:
430+
raise forms.ValidationError("The title contains characters outside the Unicode BMP, which is not currently supported")
431+
if self.session.meeting.type_id=='interim':
432+
if re.search(r'-\d{2}$', title):
433+
raise forms.ValidationError("Interim slides currently may not have a title that ends with something that looks like a revision number (-nn)")
434+
return title
435+
436+
363437
class RequestMinutesForm(forms.Form):
364438
to = MultiEmailField()
365439
cc = MultiEmailField(required=False)

0 commit comments

Comments
 (0)