Merge branch 'master' into wip-flask-one

This commit is contained in:
Sybren A. Stüvel 2018-07-13 11:43:57 +02:00
commit baa77a7de5
3 changed files with 232 additions and 18 deletions

View File

@ -33,18 +33,57 @@ log = logging.getLogger(__name__)
def shortcode(name: str):
"""Class decorator for shortcodes."""
def decorator(cls):
assert hasattr(cls, '__call__'), '@shortcode should be used on callables.'
if isinstance(cls, type):
instance = cls()
def decorator(decorated):
assert hasattr(decorated, '__call__'), '@shortcode should be used on callables.'
if isinstance(decorated, type):
as_callable = decorated()
else:
instance = cls
shortcodes.register(name)(instance)
return cls
as_callable = decorated
shortcodes.register(name)(as_callable)
return decorated
return decorator
class capcheck:
"""Decorator for shortcodes.
On call, check for capabilities before calling the function. If the user does not
have a capability, display a message insdead of the content.
kwargs:
- 'cap': Capability required for viewing.
- 'nocap': Optional, text shown when the user does not have this capability.
- others: Passed to the decorated shortcode.
"""
def __init__(self, decorated):
assert hasattr(decorated, '__call__'), '@capcheck should be used on callables.'
if isinstance(decorated, type):
as_callable = decorated()
else:
as_callable = decorated
self.decorated = as_callable
def __call__(self,
context: typing.Any,
content: str,
pargs: typing.List[str],
kwargs: typing.Dict[str, str]) -> str:
from pillar.auth import current_user
cap = kwargs.pop('cap', '')
if cap:
nocap = kwargs.pop('nocap', '')
if not current_user.has_cap(cap):
if not nocap:
return ''
html = html_module.escape(nocap)
return f'<p class="shortcode nocap">{html}</p>'
return self.decorated(context, content, pargs, kwargs)
@shortcode('test')
class Test:
def __call__(self,
@ -68,6 +107,7 @@ class Test:
@shortcode('youtube')
@capcheck
class YouTube:
log = log.getChild('YouTube')
@ -129,6 +169,7 @@ class YouTube:
@shortcode('iframe')
@capcheck
def iframe(context: typing.Any,
content: str,
pargs: typing.List[str],
@ -140,16 +181,6 @@ def iframe(context: typing.Any,
- others: Turned into attributes for the iframe element.
"""
import xml.etree.ElementTree as ET
from pillar.auth import current_user
cap = kwargs.pop('cap', '')
if cap:
nocap = kwargs.pop('nocap', '')
if not current_user.has_cap(cap):
if not nocap:
return ''
html = html_module.escape(nocap)
return f'<p class="shortcode nocap">{html}</p>'
kwargs['class'] = f'shortcode {kwargs.get("class", "")}'.strip()
element = ET.Element('iframe', kwargs)

View File

@ -0,0 +1,170 @@
"""Test that what we feed to Cerberus actually works.
This'll help us upgrade to new versions of Cerberus.
"""
import unittest
from pillar.tests import AbstractPillarTest
from bson import ObjectId
class CerberusCanaryTest(unittest.TestCase):
def _canary_test(self, validator):
groups_schema = {'name': {'type': 'string', 'required': True}}
# On error, validate_schema() raises ValidationError
validator.validate_schema(groups_schema)
# On error, validate() returns False
self.assertTrue(validator.validate({'name': 'je moeder'}, groups_schema))
self.assertFalse(validator.validate({'je moeder': 'op je hoofd'}, groups_schema))
def test_canary(self):
import cerberus
validator = cerberus.Validator()
self._canary_test(validator)
def test_our_validator_simple(self):
from pillar.api import custom_field_validation
validator = custom_field_validation.ValidateCustomFields()
self._canary_test(validator)
class ValidationTest(AbstractPillarTest):
def setUp(self):
super().setUp()
from pillar.api import custom_field_validation
self.validator = custom_field_validation.ValidateCustomFields()
self.user_id = ObjectId(8 * 'abc')
self.ensure_user_exists(self.user_id, 'Tést Üsâh')
def assertValid(self, document, schema):
with self.app.app_context():
is_valid = self.validator.validate(document, schema)
self.assertTrue(is_valid, f'errors: {self.validator.errors}')
def assertInvalid(self, document, schema):
with self.app.app_context():
is_valid = self.validator.validate(document, schema)
self.assertFalse(is_valid)
class ProjectValidationTest(ValidationTest):
def test_empty(self):
from pillar.api.eve_settings import projects_schema
self.assertInvalid({}, projects_schema)
def test_simple_project(self):
from pillar.api.eve_settings import projects_schema
project = {
'name': 'Té Ærhüs',
'user': self.user_id,
'category': 'assets',
'is_private': False,
'status': 'published',
}
self.assertValid(project, projects_schema)
def test_with_node_types(self):
from pillar.api.eve_settings import projects_schema
from pillar.api import node_types
project = {
'name': 'Té Ærhüs',
'user': self.user_id,
'category': 'assets',
'is_private': False,
'status': 'published',
'node_types': [node_types.node_type_asset,
node_types.node_type_comment]
}
self.assertValid(project, projects_schema)
class NodeValidationTest(ValidationTest):
def setUp(self):
super().setUp()
self.pid, self.project = self.ensure_project_exists()
def test_empty(self):
from pillar.api.eve_settings import nodes_schema
self.assertInvalid({}, nodes_schema)
def test_asset(self):
from pillar.api.eve_settings import nodes_schema
file_id, _ = self.ensure_file_exists()
node = {
'name': '"The Harmless Prototype™"',
'project': self.pid,
'node_type': 'asset',
'properties': {
'status': 'published',
'content_type': 'image',
'file': file_id,
},
'user': self.user_id,
'short_code': 'ABC333',
}
self.assertValid(node, nodes_schema)
def test_asset_invalid_properties(self):
from pillar.api.eve_settings import nodes_schema
file_id, _ = self.ensure_file_exists()
node = {
'name': '"The Harmless Prototype™"',
'project': self.pid,
'node_type': 'asset',
'properties': {
'status': 'invalid-status',
'content_type': 'image',
'file': file_id,
},
'user': self.user_id,
'short_code': 'ABC333',
}
self.assertInvalid(node, nodes_schema)
def test_comment(self):
from pillar.api.eve_settings import nodes_schema
file_id, _ = self.ensure_file_exists()
node = {
'name': '"The Harmless Prototype™"',
'project': self.pid,
'node_type': 'asset',
'properties': {
'status': 'published',
'content_type': 'image',
'file': file_id,
},
'user': self.user_id,
'short_code': 'ABC333',
}
node_id = self.create_node(node)
comment = {
'name': 'comment on some node',
'project': self.pid,
'node_type': 'comment',
'properties': {
'content': 'this is a comment',
'status': 'published',
},
'parent': node_id,
}
self.assertValid(comment, nodes_schema)

View File

@ -40,7 +40,7 @@ class DemoTest(unittest.TestCase):
self.assertEqual('<dl><dt>test</dt><dt>ü</dt><dd>é</dd></dl>', render('{test ü="é"}'))
class YouTubeTest(unittest.TestCase):
class YouTubeTest(AbstractPillarTest):
def test_missing(self):
from pillar.shortcodes import render
@ -104,6 +104,19 @@ class YouTubeTest(unittest.TestCase):
render('{youtube "https://www.youtube.com/watch?v=NwVGvcIrNWA" width=5 height="3"}')
)
def test_user_no_cap(self):
from pillar.shortcodes import render
with self.app.app_context():
# Anonymous user, so no subscriber capability.
self.assertEqual('', render('{youtube ABCDEF cap=subscriber}'))
self.assertEqual('', render('{youtube ABCDEF cap="subscriber"}'))
self.assertEqual(
'<p class="shortcode nocap">Aðeins áskrifendur hafa aðgang að þessu efni.</p>',
render('{youtube ABCDEF'
' cap="subscriber"'
' nocap="Aðeins áskrifendur hafa aðgang að þessu efni."}'))
class IFrameTest(AbstractPillarTest):
def test_missing_cap(self):