def test_markup_choice(self): """ Check the distinction between Creole and Markdown pages """ offering = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) memb = Member.objects.get(offering=offering, person__userid="ggbaker") p = Page(offering=offering, label="Test") p.save() v1 = PageVersion(page=p, title="T1", wikitext='A //test//.', editor=memb, comment="original page") v1.save() self.assertEqual(v1.html_contents(), '<p>A <em>test</em>.</p>') v2 = PageVersion(page=p, title="T1", wikitext='A *test*.', editor=memb, comment="original page") v2.set_markup('markdown') v2.save() self.assertEqual(v2.html_contents(), '<p>A <em>test</em>.</p>')
def test_html_safety(self): """ Check that we're handling HTML in a safe way """ html = markup_to_html('<p>Foo</em>', 'html') self.assertEqual(html, '<p>Foo</p>') html = markup_to_html('Foo<script>alert()</script>', 'html') self.assertEqual(html, 'Fooalert()') # some junky MSWord-like markup html = markup_to_html('Foo<p class="home"><Span style="font-size: 500%">bar</Span></P>', 'html', restricted=True) self.assertEqual(html, 'Foo<p>bar</p>') html = markup_to_html('A <p> </p><table cellpadding="10"><tr><td colspan=4>B</td></tr></table>', 'html', restricted=True) self.assertEqual(html, 'A <p> </p>B') # unsafe if we ask for it html = markup_to_html('Foo<script>alert()</script>', 'html', html_already_safe=True) self.assertEqual(html, 'Foo<script>alert()</script>') # PageVersions should be saved only with safe HTML offering = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) memb = Member.objects.get(offering=offering, person__userid="ggbaker") p = Page(offering=offering, label="Test") p.save() v1 = PageVersion(page=p, title="T1", wikitext='<em>Some</em> <script>HTML</script>', editor=memb) v1.set_markup('html') v1.save() self.assertEqual(v1.wikitext, '<em>Some</em> HTML')
def save(self, editor, *args, **kwargs): # create the PageVersion object: distribute the self.cleaned_data values appropriately wikitext = self.cleaned_data['markup_content'] comment = self.cleaned_data['comment'] title = self.cleaned_data['title'] pv = PageVersion(title=title, wikitext=wikitext, comment=comment, editor=editor) pv.set_markup(self.cleaned_data['_markup']) pv.set_math(self.cleaned_data['_math']) self.instance.offering = self.offering pg = super(EditPageForm, self).save(*args, **kwargs) pv.page=self.instance pv.save() return pg
def test_html_safety(self): """ Check that we're handling HTML in a safe way """ html = markup_to_html('<p>Foo</em>', 'html') self.assertEqual(html, '<p>Foo</p>') html = markup_to_html('Foo<script>alert()</script>', 'html') self.assertEqual(html, 'Fooalert()') # some junky MSWord-like markup html = markup_to_html( 'Foo<p class="home"><Span style="font-size: 500%">bar</Span></P>', 'html', restricted=True) self.assertEqual(html, 'Foo<p>bar</p>') html = markup_to_html( 'A <p> </p><table cellpadding="10"><tr><td colspan=4>B</td></tr></table>', 'html', restricted=True) self.assertEqual(html, 'A <p> </p>B') # unsafe if we ask for it html = markup_to_html('Foo<script>alert()</script>', 'html', html_already_safe=True) self.assertEqual(html, 'Foo<script>alert()</script>') # PageVersions should be saved only with safe HTML offering = CourseOffering.objects.get(slug=TEST_COURSE_SLUG) memb = Member.objects.get(offering=offering, person__userid="ggbaker") p = Page(offering=offering, label="Test") p.save() v1 = PageVersion(page=p, title="T1", wikitext='<em>Some</em> <script>HTML</script>', editor=memb) v1.set_markup('html') v1.save() self.assertEqual(v1.wikitext, '<em>Some</em> HTML')
def _pages_from_json(request, offering, data): with django.db.transaction.atomic(): try: data = data.decode('utf-8-sig') except UnicodeDecodeError: raise ValidationError("Bad UTF-8 data in file.") try: data = json.loads(data) except ValueError as e: raise ValidationError('JSON decoding error. Exception was: "' + str(e) + '"') if not isinstance(data, dict): raise ValidationError( 'Outer JSON data structure must be an object.') if 'userid' not in data or 'token' not in data: raise ValidationError( 'Outer JSON data object must contain keys "userid" and "token".' ) if 'pages' not in data: raise ValidationError( 'Outer JSON data object must contain keys "pages".') if not isinstance(data['pages'], list): raise ValidationError('Value for "pages" must be a list.') try: user = Person.objects.get(userid=data['userid']) member = Member.objects.exclude(role='DROP').get(person=user, offering=offering) except (Person.DoesNotExist, Member.DoesNotExist): raise ValidationError('Person with that userid does not exist.') if 'pages-token' not in user.config or user.config[ 'pages-token'] != data['token']: e = ValidationError('Could not validate authentication token.') e.status = 403 raise e # if we get this far, the user is authenticated and we can start processing the pages... for i, pdata in enumerate(data['pages']): if not isinstance(pdata, dict): raise ValidationError( 'Page #%i entry structure must be an object.' % (i)) if 'label' not in pdata: raise ValidationError( 'Page #%i entry does not have a "label".' % (i)) # handle changes to the Page object pages = Page.objects.filter(offering=offering, label=pdata['label']) if pages: page = pages[0] old_ver = page.current_version() else: page = Page(offering=offering, label=pdata['label']) old_ver = None # check write permissions # mock the request object enough to satisfy _check_allowed() class FakeRequest(object): is_authenticated = True fake_request = FakeRequest() fake_request.user = FakeRequest() fake_request.user.username = user.userid if old_ver: m = _check_allowed(fake_request, offering, page.can_write, page.editdate()) else: m = _check_allowed(fake_request, offering, offering.page_creators()) if not m: raise ValidationError('You can\'t edit page #%i.' % (i)) # handle Page attributes if 'can_read' in pdata: if type(pdata['can_read'] ) != str or pdata['can_read'] not in ACL_DESC: raise ValidationError( 'Page #%i "can_read" value must be one of %s.' % (i, ','.join(list(ACL_DESC.keys())))) page.can_read = pdata['can_read'] if 'can_write' in pdata: if type(pdata['can_write'] ) != str or pdata['can_write'] not in WRITE_ACL_DESC: raise ValidationError( 'Page #%i "can_write" value must be one of %s.' % (i, ','.join(list(WRITE_ACL_DESC.keys())))) if m.role == 'STUD': raise ValidationError( 'Page #%i: students can\'t change can_write value.' % (i)) page.can_write = pdata['can_write'] if 'new_label' in pdata: if type(pdata['new_label']) != str: raise ValidationError( 'Page #%i "new_label" value must be a string.' % (i)) if m.role == 'STUD': raise ValidationError( 'Page #%i: students can\'t change label value.' % (i)) if Page.objects.filter(offering=offering, label=pdata['new_label']): raise ValidationError( 'Page #%i: there is already a page with that "new_label".' % (i)) page.label = pdata['new_label'] page.save() # handle PageVersion changes ver = PageVersion(page=page, editor=member) if 'title' in pdata: if type(pdata['title']) != str: raise ValidationError( 'Page #%i "title" value must be a string.' % (i)) ver.title = pdata['title'] elif old_ver: ver.title = old_ver.title else: raise ValidationError('Page #%i has no "title" for new page.' % (i)) if 'comment' in pdata: if type(pdata['comment']) != str: raise ValidationError( 'Page #%i "comment" value must be a string.' % (i)) ver.comment = pdata['comment'] if 'use_math' in pdata: if type(pdata['use_math']) != bool: raise ValidationError( 'Page #%i "comment" value must be a boolean.' % (i)) ver.set_math(pdata['use_math']) if 'markup' in pdata: if isinstance(pdata['markup'], str): raise ValidationError( 'Page #%i "markup" value must be a string.' % (i)) ver.set_markup(pdata['markup']) if 'wikitext-base64' in pdata: if type(pdata['wikitext-base64']) != str: raise ValidationError( 'Page #%i "wikitext-base64" value must be a string.' % (i)) try: wikitext = base64.b64decode( pdata['wikitext-base64']).decode('utf8') except TypeError: raise ValidationError( 'Page #%i "wikitext-base64" contains bad base BASE64 data.' % (i)) ver.wikitext = wikitext elif 'wikitext' in pdata: if type(pdata['wikitext']) != str: raise ValidationError( 'Page #%i "wikitext" value must be a string.' % (i)) ver.wikitext = pdata['wikitext'] elif old_ver: ver.wikitext = old_ver.wikitext else: raise ValidationError( 'Page #%i has no wikitext for new page.' % (i)) ver.save() return user
def _pages_from_json(request, offering, data): with django.db.transaction.atomic(): try: data = data.decode('utf-8-sig') except UnicodeDecodeError: raise ValidationError("Bad UTF-8 data in file.") try: data = json.loads(data) except ValueError as e: raise ValidationError('JSON decoding error. Exception was: "' + str(e) + '"') if not isinstance(data, dict): raise ValidationError('Outer JSON data structure must be an object.') if 'userid' not in data or 'token' not in data: raise ValidationError('Outer JSON data object must contain keys "userid" and "token".') if 'pages' not in data: raise ValidationError('Outer JSON data object must contain keys "pages".') if not isinstance(data['pages'], list): raise ValidationError('Value for "pages" must be a list.') try: user = Person.objects.get(userid=data['userid']) member = Member.objects.exclude(role='DROP').get(person=user, offering=offering) except (Person.DoesNotExist, Member.DoesNotExist): raise ValidationError('Person with that userid does not exist.') if 'pages-token' not in user.config or user.config['pages-token'] != data['token']: e = ValidationError('Could not validate authentication token.') e.status = 403 raise e # if we get this far, the user is authenticated and we can start processing the pages... for i, pdata in enumerate(data['pages']): if not isinstance(pdata, dict): raise ValidationError('Page #%i entry structure must be an object.' % (i)) if 'label' not in pdata: raise ValidationError('Page #%i entry does not have a "label".' % (i)) # handle changes to the Page object pages = Page.objects.filter(offering=offering, label=pdata['label']) if pages: page = pages[0] old_ver = page.current_version() else: page = Page(offering=offering, label=pdata['label']) old_ver = None # check write permissions # mock the request object enough to satisfy _check_allowed() class FakeRequest(object): is_authenticated = True fake_request = FakeRequest() fake_request.user = FakeRequest() fake_request.user.username = user.userid if old_ver: m = _check_allowed(fake_request, offering, page.can_write, page.editdate()) else: m = _check_allowed(fake_request, offering, offering.page_creators()) if not m: raise ValidationError('You can\'t edit page #%i.' % (i)) # handle Page attributes if 'can_read' in pdata: if type(pdata['can_read']) != str or pdata['can_read'] not in ACL_DESC: raise ValidationError('Page #%i "can_read" value must be one of %s.' % (i, ','.join(list(ACL_DESC.keys())))) page.can_read = pdata['can_read'] if 'can_write' in pdata: if type(pdata['can_write']) != str or pdata['can_write'] not in WRITE_ACL_DESC: raise ValidationError('Page #%i "can_write" value must be one of %s.' % (i, ','.join(list(WRITE_ACL_DESC.keys())))) if m.role == 'STUD': raise ValidationError('Page #%i: students can\'t change can_write value.' % (i)) page.can_write = pdata['can_write'] if 'new_label' in pdata: if type(pdata['new_label']) != str: raise ValidationError('Page #%i "new_label" value must be a string.' % (i)) if m.role == 'STUD': raise ValidationError('Page #%i: students can\'t change label value.' % (i)) if Page.objects.filter(offering=offering, label=pdata['new_label']): raise ValidationError('Page #%i: there is already a page with that "new_label".' % (i)) page.label = pdata['new_label'] page.save() # handle PageVersion changes ver = PageVersion(page=page, editor=member) if 'title' in pdata: if type(pdata['title']) != str: raise ValidationError('Page #%i "title" value must be a string.' % (i)) ver.title = pdata['title'] elif old_ver: ver.title = old_ver.title else: raise ValidationError('Page #%i has no "title" for new page.' % (i)) if 'comment' in pdata: if type(pdata['comment']) != str: raise ValidationError('Page #%i "comment" value must be a string.' % (i)) ver.comment = pdata['comment'] if 'use_math' in pdata: if type(pdata['use_math']) != bool: raise ValidationError('Page #%i "comment" value must be a boolean.' % (i)) ver.set_math(pdata['use_math']) if 'markup' in pdata: if isinstance(pdata['markup'], str): raise ValidationError('Page #%i "markup" value must be a string.' % (i)) ver.set_markup(pdata['markup']) if 'wikitext-base64' in pdata: if type(pdata['wikitext-base64']) != str: raise ValidationError('Page #%i "wikitext-base64" value must be a string.' % (i)) try: wikitext = base64.b64decode(pdata['wikitext-base64']).decode('utf8') except TypeError: raise ValidationError('Page #%i "wikitext-base64" contains bad base BASE64 data.' % (i)) ver.wikitext = wikitext elif 'wikitext' in pdata: if type(pdata['wikitext']) != str: raise ValidationError('Page #%i "wikitext" value must be a string.' % (i)) ver.wikitext = pdata['wikitext'] elif old_ver: ver.wikitext = old_ver.wikitext else: raise ValidationError('Page #%i has no wikitext for new page.' % (i)) ver.save() return user